import cats.free.Free
import cats.free.Free.liftF
import freek._
sealed trait DSL[R]
case class Get(key: String) extends DSL[Option[Int]]
case class Put(key: String, value: Int) extends DSL[Int]
type Program = DSL :|: FXNil
def get(key: String): Free[Program#Cop, Option[Int]] = Get(key).freek[Program]
sealed trait DSLExtension[A]
case class Log(message: String) extends DSLExtension[Unit]
type Extended = DSLExtension :|: Program
def getAndLog(key: String): Free[Extended#Cop, Option[Int]] = for {
maybeFound <- get(key).expand[Extended]
_ <- Log(s"Get performed with result $maybeFound").freek[Extended]
} yield maybeFound
showing we can easily combine "local" DSLs. But there is one issue. The first DSL must be written with that in mind. If it was written without Freek you cannot expand it later without rewriting (at least I do not know how). I would expect the following to work:
import cats.free.Free
import cats.free.Free.liftF
import freek._
sealed trait DSL[R]
case class Get(key: String) extends DSL[Option[Int]]
case class Put(key: String, value: Int) extends DSL[Int]
def get(key: String): Free[DSL, Option[Int]] = liftF(Get(key))
sealed trait DSLExtension[A]
case class Log(message: String) extends DSLExtension[Unit]
type Extended = DSLExtension :|: DSL :|: FXNil
def getAndLog(key: String): Free[Extended#Cop, Option[Int]] = for {
maybeFound <- get(key).expand[Extended] // ar any other method
_ <- Log(s"Get performed with result $maybeFound").freek[Extended]
} yield maybeFound
The following program compiles and works:
showing we can easily combine "local" DSLs. But there is one issue. The first DSL must be written with that in mind. If it was written without Freek you cannot expand it later without rewriting (at least I do not know how). I would expect the following to work: