0

I am using cats library and want to convert Free Monad to Free Applicative.

We have a lot of code in Free monads.. But now some parts of the application has to run in parallel. There are options to use Tagless or Frees.io instead of Free.. But that will be a huge change...

This is an example DSL:

sealed trait DSLAction[A]

case class GetCustomer(request: Boolean) extends DSLAction[String]

case class GetSize(request: Boolean) extends DSLAction[Int]

val f1: Free[DSLAction, String] = liftF(GetCustomer(true))
val f2: Free[DSLAction, Int] = liftF(GetSize(true))
val f3: Free[DSLAction, Int] = liftF(GetSize(false))

  val interpreter: DSLAction ~> Id = {
    λ[DSLAction ~> Id] {
      case GetCustomer(_: Boolean) => {
        "hello"
      }
      case GetSize(_: Boolean) => {
        123
      }
    }
  }

Cats library provides a way to convert FreeApplicative to Free monad using .monad()

However, I want to convert Free to FreeApplicative to use them in for comprehension

I want to define a method toApplicative () that does the job...

type FEF[A] = FreeApplicative[DSLAction, A]

val f1AP: FEF[String] = toApplicative(f1)
val f2AP: FEF[Int] = toApplicative(f2)


val prog = for {
    a <- (f1AP, f2AP).mapN { case (l, r) => l + r }.monad
    b <- f3
  } yield {
    (a, b)
  }


  prog.foldMap(interpreter)

I did try to implement something.. But not sure how to define flatMap and tailRec methods..

Or there may be another way

  implicit val myConvertor = new Monad[FEF] {

    override def pure[A](x: A): FEF[A] = FreeApplicative.pure[DSLAction,A](x)

    override def flatMap[A, B](fa: FEF[A])(f: A => FEF[B]): FEF[B] = ???

    override def tailRecM[A, B](a: A)(f: A => FEF[Either[A, B]]): FEF[B] = ???
  }


  final def toApplicative[F, A](free: Free[DSLAction, A]) =
    free.foldMap[FreeApplicative[DSLAction, ?]] {
      λ[FunctionK[DSLAction, FEF]](fa => FreeApplicative.lift(fa))
    }

Thanks

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
abhishek
  • 31
  • 1
  • 6

1 Answers1

1

Try

implicit val myConvertor = new Monad[FEF] {
  override def pure[A](x: A): FEF[A] = FreeApplicative.pure[DSLAction,A](x)
  override def flatMap[A, B](fa: FEF[A])(f: A => FEF[B]): FEF[B] = toApplicative(Monad[Free[DSLAction, ?]].flatMap(fa.monad)(a => f(a).monad))
  override def tailRecM[A, B](a: A)(f: A => FEF[Either[A, B]]): FEF[B] = toApplicative(Monad[Free[DSLAction, ?]].tailRecM(a)(a => f(a).monad))
}

Actually toApplicative doesn't work since it needs Monad[FreeApplicative[DSLAction,?]] and such instance doesn't exist, FreeApplicative[DSLAction,?] is an Applicative, not a Monad (on contrary FreeApplicative#monad works because there is Monad[Free[DSLAction,?]]).

https://typelevel.org/cats/datatypes/freeapplicative.html#differences-from-free

So far everything we’ve been doing has been not much different from Free - we’ve built an algebra and interpreted it. However, there are some things FreeApplicative can do that Free cannot.

So I guess there is transform from FreeApplicative[DSLAction,?] to Free[DSLAction,?] but there is no transform from Free[DSLAction,?] to FreeApplicative[DSLAction,?]. Generally you can't get thing that can do more (FreeApplicative can do both parallel and consecutive execution) from thing that can do less (Free can do only consecutive execution).

Free is a Monad, so moreover it's an Applicative but this Applicative instance is different from the Applicative instance that FreeApplicative is.

A quote from @BogdanVakulenko's link at reddit

it's genuinely impossible to turn a monad into an applicative in the general case because many applicatives (e.g. Const[M: Monoid]) don't form monads. If your monad preserves the distinction between applicative operations and monadic operations then it's no longer free

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66