Monad Transformer

Формальное определение

Некоторые виды монад можно прокидывать внутрь других монад, тем самым трансформируя их. Т.е. для этих видов монад (F) доступна операция lift - M[A] -> M[F[A]]

Определение в виде кода на Scala

trait MonadTransformer[T[_[_], _], M[_]](using mMonad: Monad[M], tMonad: Monad[[X] =>> T[M, X]]):
  def lift[A](ma: M[A]): T[M, A]

Примеры

"Обертка"

final case class IdT[M[_], A](run: M[Id[A]])

given idtMonad[M[_]](using outerMonad: Monad[M]): Monad[[X] =>> IdT[M, X]] with
  override def unit[A](a: => A): IdT[M, A] =
    IdT[M, A](outerMonad.unit(Id(a)))
  extension [A](fa: IdT[M, A])
    override def flatMap[B](f: A => IdT[M, B]): IdT[M, B] =
      IdT[M, B](fa.run.flatMap(ida => f(ida.value).run))

given idtMonadTransformer[M[_]](using outerMonad: Monad[M]): MonadTransformer[IdT, M] with
  override def lift[A](ma: M[A]): IdT[M, A] =
    IdT[M, A](ma.map(Id(_)))

Option

final case class OptionT[M[_], A](run: M[Option[A]])

given optionTMonad[M[_]](using outerMonad: Monad[M]): Monad[[X] =>> OptionT[M, X]] with
  override def unit[A](a: => A): OptionT[M, A] =
    OptionT[M, A](outerMonad.unit(Some(a)))

  extension [A](fa: OptionT[M, A])
    override def flatMap[B](f: A => OptionT[M, B]): OptionT[M, B] =
      OptionT(
        fa.run.flatMap {
          case Some(value) => f(value).run
          case None        => outerMonad.unit(None)
        }
      )
      
given optionTMonadTransformer[M[_]](using outerMonad: Monad[M]): MonadTransformer[OptionT, M] with
  override def lift[A](ma: M[A]): OptionT[M, A] =
    OptionT[M, A](ma.map(Some(_)))      

Writer - функциональный журнал

final case class WriterT[M[_], W, A](run: () => M[(W, A)])

given writerTMonad[M[_], W](using outerMonad: Monad[M], outerMonoid: Monoid[W]): Monad[[X] =>> WriterT[M, W, X]] with
  override def unit[A](a: => A): WriterT[M, W, A] =
    WriterT[M, W, A](() => outerMonad.unit((outerMonoid.empty, a)))

  extension [A](fa: WriterT[M, W, A])
    override def flatMap[B](f: A => WriterT[M, W, B]): WriterT[M, W, B] =
      WriterT[M, W, B] { () =>
        fa.run().flatMap { case (wa, a) =>
          f(a).run().map { case (wb, b) => (outerMonoid.combine(wa, wb), b) }
        }
      }
      
given writerTMonadTransformer[M[_], W](using
    outerMonad: Monad[M],
    outerMonoid: Monoid[W]
): MonadTransformer[[Y[_], X] =>> WriterT[Y, W, X], M] with
  override def lift[A](ma: M[A]): WriterT[M, W, A] =
    WriterT[M, W, A](() => ma.map(a => (outerMonoid.empty, a)))      

State - функциональное состояние

final case class StateT[M[_], S, A](run: S => M[(S, A)])

given stateTMonad[M[_], S](using outerMonad: Monad[M]): Monad[[X] =>> StateT[M, S, X]] with
  override def unit[A](a: => A): StateT[M, S, A] =
    StateT[M, S, A](s => outerMonad.unit((s, a)))

  extension [A](fa: StateT[M, S, A])
    override def flatMap[B](f: A => StateT[M, S, B]): StateT[M, S, B] =
      StateT[M, S, B] { s =>
        fa.run(s).flatMap { case (s1, a) => f(a).run(s1) }
      }
      
given stateTMonadTransformer[M[_], S](using outerMonad: Monad[M]): MonadTransformer[[Y[_], X] =>> StateT[Y, S, X], M] with
  override def lift[A](ma: M[A]): StateT[M, S, A] =
    StateT[M, S, A](s => ma.map(a => (s, a)))      

Ссылки: