Created
April 27, 2016 04:07
-
-
Save lancegatlin/65d09d51d7b415eb94a3f408363bff3b 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
package org.lancegatlin | |
import scala.language.higherKinds | |
import scalaz._ | |
import Id._ | |
import std._ | |
import scalaz.effect.IO | |
import scala.concurrent.Future | |
import scala.concurrent.ExecutionContext.Implicits._ | |
object test { | |
type LogWriter[A] = Writer[List[String], A] | |
// Scalaz newb I guess not sure where these are implemented by scalaz | |
implicit object semigroup_ListString extends Semigroup[List[String]] { | |
override def append(f1: List[String], f2: => List[String]): List[String] = | |
f1 ++ f2 | |
} | |
implicit object monad_LogWriter extends Monad[LogWriter] { | |
override def bind[A, B](fa:LogWriter[A])(f: (A) => LogWriter[B]): LogWriter[B] = | |
fa.flatMap(f) | |
override def point[A](a: => A): LogWriter[A] = | |
Writer(Nil,a) | |
} | |
/* ******** Monad for Akka Future (naughty uses global context) ********** */ | |
implicit object monad_Future extends Monad[Future] { | |
override def map[A, B](fa: Future[A])(f: (A) => B): Future[B] = fa.map(f) | |
override def apply[A, B](fa: Future[A])(f: (A) => B): Future[B] = fa.map(f) | |
override def point[A](a: => A): Future[A] = Future(a) | |
override def bind[A, B](fa: Future[A])(f: (A) => Future[B]): Future[B] = fa.flatMap(f) | |
} | |
/* ******** Lift ********** */ | |
abstract class Lift[M1[_]:Monad,M2[_]:Monad] { | |
def lift[A](m:M1[A]) : M2[A] | |
} | |
object Lift { | |
def apply[M1[_],M2[_]](implicit L:Lift[M1,M2]) = L | |
} | |
implicit def lift_Id[M[_]](implicit M:Monad[M]) = new Lift[Id,M] { | |
def lift[A](m:Id[A]) : M[A] = { | |
M.point(m) | |
} | |
} | |
/* ******** Generic Monad Ops ********** */ | |
implicit class PimpMyMonad[M[_],A](val self: M[A]) extends AnyVal { | |
def map[B](f: A => B)(implicit M:Monad[M]) : M[B] = | |
M.map(self)(f) | |
def flatMap[B](f: A => M[B])(implicit M:Monad[M]) : M[B] = | |
M.bind(self)(f) | |
def lift[N[_]](implicit L:Lift[M,N]) : N[A] = L.lift(self) | |
} | |
/* ******** Lift a generic service ********** */ | |
trait LiftService[S[_[_]]] { | |
def lift[E[_],F[_]]( | |
s: S[E] | |
)(implicit | |
E:Monad[E], | |
F:Monad[F], | |
L:Lift[E,F] | |
) : S[F] | |
} | |
implicit class PimpMyService[S[_[_]],E[_]](val self:S[E]) extends AnyVal { | |
def liftS[F[_]](implicit E:Monad[E],F:Monad[F],L:Lift[E,F],LS:LiftService[S]) : S[F] = | |
LS.lift[E,F](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[E[_], F[_]]( | |
s: Logger[E] | |
)(implicit | |
E: Monad[E], | |
F: Monad[F], | |
L: Lift[E,F]) = | |
new Logger[F] { | |
override def info(msg: => String): F[Unit] = | |
L.lift(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[E[_], F[_]]( | |
s: Console[E] | |
)(implicit | |
E: Monad[E], | |
F: Monad[F], | |
L: Lift[E,F]) = | |
new Console[F] { | |
override def print(msg: String): F[Unit] = | |
L.lift(s.print(msg)) | |
} | |
} | |
/* ******** Fake db ********** */ | |
trait Db[E[_]] { | |
def read() : E[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 | |
E: Monad[E], | |
F: Monad[F], | |
L: Lift[E,F]) = | |
new Db[F] { | |
override def read(): F[Int] = | |
L.lift(s.read()) | |
} | |
} | |
/* ******** Test service ********** */ | |
trait MyService[E[_]] { self => | |
def myMethod(i: Int) : E[String] | |
} | |
class MyServiceImpl[E[_]]( | |
db: Db[E], | |
logger: Logger[E], | |
console: Console[E] | |
)(implicit | |
E:Monad[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[E[_], F[_]]( | |
s: MyService[E] | |
)(implicit | |
E: Monad[E], | |
F: Monad[F], | |
L: Lift[E,F] | |
) = | |
new MyService[F] { | |
override def myMethod(i: Int): F[String] = | |
L.lift(s.myMethod(i)) | |
} | |
} | |
/* ******** Do interesting things here ********** */ | |
val immediateSvc = | |
new MyServiceImpl[Id]( | |
MagicDbImpl, // already returns Futures | |
ImmediateLogger, // lift into Future | |
ImmediateConsole // lift into Future | |
) | |
val asyncSvc = | |
new MyServiceImpl[Future]( | |
AsyncDbImpl, // already returns Futures | |
ImmediateLogger.liftS, // lift service into Future | |
ImmediateConsole.liftS // lift service into Future | |
) | |
val writerSvc = | |
new MyServiceImpl[LogWriter]( | |
MagicDbImpl.liftS, | |
LogWriterLogger, | |
ImmediateConsole.liftS | |
) | |
val ioSvc = | |
new MyServiceImpl[IO]( | |
MagicDbImpl.liftS, | |
ImmediateLogger.liftS, | |
IOConsole | |
) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment