Skip to content

Instantly share code, notes, and snippets.

@mandubian
Created December 16, 2016 22:45
Show Gist options
  • Save mandubian/4365085d947d609b18af95df39ea569b to your computer and use it in GitHub Desktop.
Save mandubian/4365085d947d609b18af95df39ea569b to your computer and use it in GitHub Desktop.
package papersplease2
import freek._
import cats.free.Free
import cats.{~>, Id}
import cats.data.Xor
case class Person(name: String)
case class Picture(person: Person)
case class Passedport(person: Person)
case class FingerPrint(person: Person)
sealed trait Log[A]
object Log {
final case class Info(msg: String) extends Log[Unit]
final case class Error(msg: String) extends Log[Unit]
}
sealed trait PictureError
sealed trait PictureService[A]
object PictureService {
final case class Shoot(dude: Person) extends PictureService[Xor[PictureError, Picture]]
}
sealed trait PersonService[A]
object PersonService {
final case class AskPassedport(dude: Person) extends PersonService[Option[Passedport]]
final case class AskFingerPrint(dude: Person) extends PersonService[Option[FingerPrint]]
}
sealed trait PoliceService[A]
object PoliceService {
final case class Eject(dude: Person) extends PoliceService[Unit]
}
sealed trait IDResult
case object OK extends IDResult
case object KO extends IDResult
sealed trait IDError
sealed trait IDService[A]
object IDService {
final case class Check(pic: Picture, passedport: Passedport, fingers: FingerPrint) extends IDService[Xor[IDError, IDResult]]
}
sealed trait PPResult
final case object Passed extends PPResult
final case object Rejected extends PPResult
sealed trait Logic[A]
object Logic {
final case class Dystopian(person: Person) extends Logic[PPResult]
}
object PapersPlease {
import cats.instances.option._
// import cats.instances.list._
// import cats.syntax.traverse._
type PRG = Log :|: PictureService :|: PersonService :|: PoliceService :|: IDService :|: Logic :|: NilDSL
val PRG = DSL.Make[PRG]
type O = Xor[IDError, ?] :&: Xor[PictureError, ?] :&: Option :&: Bulb
def logic(dude: Person): Free[PRG.Cop, O#Layers[PPResult]] = (for {
pic <- PictureService.Shoot(dude) .freek[PRG].onionT[O]
passedport <- PersonService.AskPassedport(dude) .freek[PRG].onionT[O]
fingers <- PersonService.AskFingerPrint(dude) .freek[PRG].onionT[O]
status <- IDService.Check(pic, passedport, fingers) .freek[PRG].onionT[O]
res <- status match {
case OK => for {
_ <- Log.Info(s"$dude OK") .freek[PRG].onionT[O]
} yield (Passed:PPResult)
case KO => for {
_ <- Log.Error(s"$dude KO") .freek[PRG].onionT[O]
res <- Logic.Dystopian(dude) .freek[PRG].onionT[O]
_ <- PoliceService.Eject(dude) .freek[PRG].onionT[O]
} yield (res)
}
} yield (res)).value
}
sealed trait SecretPoliceService[A]
object SecretPoliceService {
case object Deny extends SecretPoliceService[PPResult]
case object Abduct extends SecretPoliceService[PPResult]
}
object ErrorManager {
type PRG = Log :|: SecretPoliceService :|: NilDSL
val PRG = DSL.Make[PRG]
def apply(dude: Person, res: PapersPlease.O#Layers[PPResult]): Free[PRG.Cop, PPResult] =
res match {
case Xor.Left(idError) => SecretPoliceService.Deny.freek[PRG]
case Xor.Right(Xor.Left(picError)) => SecretPoliceService.Deny.freek[PRG]
case Xor.Right(Xor.Right(res)) => res match {
case Some(res) => Free.pure[PRG.Cop, PPResult](Passed)
case None => SecretPoliceService.Abduct.freek[PRG]
}
}
}
object PapersPleaseWithErrorManager {
type PRG = PapersPlease.PRG :||: ErrorManager.PRG
val PRG = DSL.Make[PRG]
def logic(dude: Person): Free[PRG.Cop, PPResult] = for {
res0 <- PapersPlease.logic(dude).expand[PRG]
res1 <- ErrorManager(dude, res0).expand[PRG]
} yield (res1)
}
sealed trait Foo3[A]
final case class Bar31(s: String) extends Foo3[Float]
object Interpreters {
val Log2Id = new (Log ~> Id) {
def apply[A](l: Log[A]): Id[A] = l match {
case Log.Info(msg) => println(s"[info] $msg")
case Log.Error(msg) => println(s"[error] $msg")
}
}
val PictureService2Id = new (PictureService ~> Id) {
def apply[A](l: PictureService[A]): Id[A] = l match {
case PictureService.Shoot(p) => Xor.Right(Picture(p))
}
}
import fs2.Task
val Log2Task0 = new (Log ~> Task) {
def apply[A](l: Log[A]): Task[A] = l match {
case Log.Info(msg) => Task.now(println(s"[info] $msg"))
case Log.Error(msg) => Task.now(println(s"[error] $msg"))
}
}
class Camera {
def shoot: Task[Picture] = ???
}
class PictureService2Task(
camera: Camera
) extends (PictureService ~> Task) {
def apply[A](l: PictureService[A]): Task[A] = l match {
case PictureService.Shoot(p) => camera.shoot.map(Xor.Right(_))
}
}
val Log2Task: Log ~> Task = ???
val PictureService2Task: PictureService ~> Task = ???
val PersonService2Task: PersonService ~> Task = ???
val PoliceService2Task: PoliceService ~> Task = ???
val IDService2Task: IDService ~> Task = ???
val Logic2Task: Logic ~> Task = ???
val SecretPoliceService2Task: SecretPoliceService ~> Task = ???
val interpreter = Log2Task :&: PictureService2Task :&: PersonService2Task :&: PoliceService2Task :&: IDService2Task :&: Logic2Task :&: SecretPoliceService2Task
val interpreter2 = SecretPoliceService2Task :&: interpreter
val dude: Person = ???
val program: Free[PapersPleaseWithErrorManager.PRG.Cop, PPResult] = PapersPleaseWithErrorManager.logic(dude)
implicit val monad: cats.Monad[Task] = ???
val res: Task[PPResult] = program.interpret(interpreter)
}
@bayarmunkh
Copy link

Thanks for the example.

I have this example:

object Test {

  import cats.free.Free
  import cats.instances.either._
  import cats.instances.list._
  import cats.instances.option._
  import freek._

  case class Hobby(name: String)
  case class User(id: Int, hobbyType: Int, hobbies: List[Hobby])

  object User {
    sealed trait DSL[A]
    case class Get(id: Int) extends DSL[User]
    case class Update(id: Int, hobbies: List[Hobby]) extends DSL[Option[User]]
  }

  object Hobby {
    sealed trait DSL[A]
    case class GetList(typeId: Int) extends DSL[List[Hobby]]
  }

  type PRG = User.DSL :|: Hobby.DSL :|: NilDSL
  val PRG = freek.DSL.Make[PRG]
  type O = Either[Throwable, ?] :&: Option :&: Bulb

  // Preferred output is Either[Throwable, Option[User]]
  def program(id: Int): OnionT[Free, PRG.Cop, O, User] = for {
    user    <- User.Get(id).freeko[PRG, O]
    hobbies <- Hobby.GetList(user.hobbyType).freeko[PRG, O]
    updated <- User.Update(id, hobbies).freeko[PRG, O]
  } yield updated

}

I'm getting could not find implicit value for parameter lifter2: freek.Lifter2.Aux[List[Test.Hobby],Test.O,Test.Hobby] compile error.
Any idea what i'm missing here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment