Skip to content

Instantly share code, notes, and snippets.

@aryairani
Forked from lancegatlin/generic service
Last active April 27, 2016 07:41
Show Gist options
  • Save aryairani/2555425ecf0d3c02d033eca1a63ecfbb to your computer and use it in GitHub Desktop.
Save aryairani/2555425ecf0d3c02d033eca1a63ecfbb to your computer and use it in GitHub Desktop.
scalaVersion := "2.11.7"
libraryDependencies += "org.scalaz" %% "scalaz-effect" % "7.2.2"
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