-
-
Save aryairani/2555425ecf0d3c02d033eca1a63ecfbb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
scalaVersion := "2.11.7" | |
libraryDependencies += "org.scalaz" %% "scalaz-effect" % "7.2.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.lancegatlin | |
import scala.language.higherKinds | |
import scalaz._, Scalaz._ | |
import scalaz.effect.IO | |
import scala.concurrent.Future | |
import scala.concurrent.ExecutionContext.Implicits._ | |
object test extends App { | |
type LogWriter[A] = Writer[List[String], A] | |
// imported automatically with "import Scalaz._", but for reference: | |
// val semigroup_ListString: Semigroup[List[String]] = scalaz.std.list.listMonoid[String] | |
// val monad_LogWriter: Monad[LogWriter] = scalaz.WriterT.writerTMonad[Id, List[String]](scalaz.std.list.listMonoid[String], scalaz.Id.id) | |
// val monad_Future: Monad[Future] = scalaz.std.scalaFuture.futureInstance // (naughty uses global context) | |
/* ******** Lift ********** */ | |
// Lift looks like `NaturalTransformation` aka `~>` | |
implicit def lift_Id[M[_]: Monad]: Id ~> M = new (Id ~> M) { | |
def apply[A](fa: Id[A]): M[A] = fa.point[M] // syntax from scalaz.syntax.monad._ | |
} | |
/* ******** Generic Monad Ops ********** */ | |
// import scalaz.Scalaz._ or scalaz.syntax.monad._ | |
/* ******** Lift a generic service ********** */ | |
trait LiftService[S[_[_]]] { | |
def lift[F[_], G[_]](s: S[F])(implicit t: F ~> G) : S[G] | |
} | |
implicit class PimpMyService[S[_[_]], F[_]](val self: S[F]) extends AnyVal { | |
def liftS[G[_]](implicit L: F ~> G, LS: LiftService[S]): S[G] = | |
LS.lift[F, G](self) | |
} | |
/* ******** Logger ********** */ | |
trait Logger[E[_]] { | |
def info(msg: => String) : E[Unit] | |
} | |
object ImmediateLogger extends Logger[Id] { | |
override def info(msg: => String): Id[Unit] = | |
println(s"[INFO] $msg") | |
} | |
object LogWriterLogger extends Logger[LogWriter] { | |
override def info(msg: => String): LogWriter[Unit] = | |
Writer(s"[INFO] $msg" :: Nil,()) | |
} | |
// Note: could macro implement this | |
implicit object liftService_Logger extends LiftService[Logger] { | |
override def lift[F[_], G[_]](s: Logger[F])(implicit L: F ~> G) = | |
new Logger[G] { | |
override def info(msg: => String): G[Unit] = | |
L(s.info(msg)) | |
} | |
} | |
/* ******** Console ********** */ | |
trait Console[E[_]] { | |
def print(msg: String) : E[Unit] | |
} | |
object ImmediateConsole extends Console[Id] { | |
override def print(msg: String): Id[Unit] = | |
println(msg) | |
} | |
object IOConsole extends Console[IO] { | |
override def print(msg: String): IO[Unit] = | |
IO(println(msg)) | |
} | |
implicit object liftService_Console extends LiftService[Console] { | |
override def lift[F[_], G[_]](s: Console[F])(implicit L: F ~> G) = | |
new Console[G] { | |
override def print(msg: String): G[Unit] = | |
L(s.print(msg)) | |
} | |
} | |
/* ******** Fake db ********** */ | |
trait Db[F[_]] { | |
def read() : F[Int] | |
} | |
object MagicDbImpl extends Db[Id] { | |
override def read(): Id[Int] = 2 | |
} | |
object AsyncDbImpl extends Db[Future] { | |
override def read(): Future[Int] = Future { | |
Thread.sleep(3000) | |
2 | |
} | |
} | |
implicit object liftService_Db extends LiftService[Db] { | |
override def lift[E[_], F[_]](s: Db[E])(implicit L: E ~> F) = | |
new Db[F] { | |
override def read(): F[Int] = | |
L(s.read()) | |
} | |
} | |
/* ******** Test service ********** */ | |
trait MyService[E[_]] { | |
def myMethod(i: Int) : E[String] | |
} | |
class MyServiceImpl[E[_]: Monad]( | |
db: Db[E], | |
logger: Logger[E], | |
console: Console[E] | |
) extends MyService[E] { | |
override def myMethod(i: Int): E[String] = { | |
for { | |
j <- db.read() | |
_ <- logger.info(s"read $j from db") | |
k = 2 | |
_ <- console.print(s"i=$i j=$j k=$k") | |
} yield (i * j * k).toString | |
} | |
} | |
implicit object liftService_MyService extends LiftService[MyService] { | |
override def lift[F[_], G[_]](s: MyService[F])(implicit L: F ~> G) = | |
new MyService[G] { | |
override def myMethod(i: Int): G[String] = | |
L(s.myMethod(i)) | |
} | |
} | |
/* ******** Do interesting things here ********** */ | |
val immediateSvc = | |
new MyServiceImpl[Id]( | |
MagicDbImpl, | |
ImmediateLogger, | |
ImmediateConsole | |
) | |
val asyncSvc = | |
new MyServiceImpl[Future]( | |
AsyncDbImpl, // already returns Futures | |
ImmediateLogger.liftS(lift_Id[Future], implicitly), // lift service into Future | |
ImmediateConsole.liftS(lift_Id[Future], implicitly) // lift service into Future | |
) | |
val writerSvc = | |
new MyServiceImpl[LogWriter]( | |
MagicDbImpl.liftS(lift_Id[LogWriter], implicitly), | |
LogWriterLogger, | |
ImmediateConsole.liftS(lift_Id[LogWriter], implicitly) | |
) | |
val ioSvc = | |
new MyServiceImpl[IO]( | |
MagicDbImpl.liftS(lift_Id[IO], implicitly), | |
ImmediateLogger.liftS(lift_Id[IO], implicitly), | |
IOConsole | |
) | |
println(s"immediateSvc.myMethod(1): ${immediateSvc.myMethod(1)}") | |
println(s"asyncSvc.myMethod(2): ${asyncSvc.myMethod(2)}") | |
println(s"writerSvc.myMethod(3): ${writerSvc.myMethod(3)}") | |
println(s"ioSvc.myMethod(4): ${ioSvc.myMethod(4)}") | |
class MyServiceImpl2[E[_]: Monad]( | |
db: Db[E], | |
logger: Logger[E], | |
console: Console[E] | |
) extends MyService[E] { | |
override def myMethod(i: Int): E[String] = { | |
logger.info("hello!").map(_ => "output") | |
} | |
} | |
val ioSvc2 = | |
new MyServiceImpl2[IO](null, ImmediateLogger.liftS(lift_Id[IO], implicitly), null) | |
println(s"ioSvc2.myMethod(1): ${ioSvc2.myMethod(1)}") | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment