Skip to content

Instantly share code, notes, and snippets.

@yasuabe
Last active February 19, 2019 01:07
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save yasuabe/3641f71324cd60044ab75edf324bb89b to your computer and use it in GitHub Desktop.
oop to fp - free & reader monads
import cats.data.{ReaderT, Writer}
import cats.free.Free
import cats.~>
// domain layer -----------------------------------------------------------
case class Movie(id: Int, title: String)
sealed trait Query[A]
case class GetMovie(id: Int) extends Query[Option[Movie]]
type QueryF[T] = Free[Query, T]
trait MovieRepo {
def getMovie(id: Int): QueryF[Option[Movie]]
}
trait Env {
def movieRepo: MovieRepo
}
type EnvReader[T] = ReaderT[QueryF, Env, T]
object MovieRepoOps {
def getMovie(id: Int): QueryF[Option[Movie]] = Free.liftF(GetMovie(id))
}
object MovieService {
def getMovie(id: Int): EnvReader[Option[Movie]] =
ReaderT (_.movieRepo.getMovie(id))
}
// application layer ------------------------------------------------------
import monix.eval.Task
val db = Map[Int, Movie](42 -> Movie(42, "A Movie"))
def taskInterpreter: Query ~> Task = new (Query ~> Task) {
def apply[A](fa: Query[A]): Task[A] = fa match {
case GetMovie(id) => Task(db.get(id))
}
}
val productEnv = new Env {
def movieRepo: MovieRepo = id => MovieRepoOps.getMovie(id)
}
val program = MovieService.getMovie(42)
.run(productEnv)
.foldMap(taskInterpreter)
// rumtime layer -------------------------------------------------
import scala.concurrent.Await
import monix.execution.Scheduler.Implicits.global
import scala.concurrent.duration._
Await.result(program.runToFuture, 1.second)
// Some(Movie(42,A Movie))
// test environment --------------------------------------------------------
import cats.syntax.writer._
import cats.instances.vector._
type TestWriter[T] = Writer[Vector[Any], T]
def testInterpreter2: Query ~> TestWriter =
new (Query ~> TestWriter) {
def apply[A](q: Query[A]): TestWriter[A] =
q match {
case GetMovie(id) => Option(Movie(1, "A Movie")).writer(Vector(q))
}
}
val testEnv = new Env {
def movieRepo: MovieRepo = _ => MovieRepoOps.getMovie(-1)
}
val (l, v) = MovieService.getMovie(-1)
.run(testEnv)
.foldMap(testInterpreter2)
.run
// l: Vector[Any] = Vector(GetMovie(-1))
// v: Option[Movie] = Some(Movie(1,A Movie))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment