Skip to content

Instantly share code, notes, and snippets.

@lancegatlin
Created April 27, 2016 04:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lancegatlin/65d09d51d7b415eb94a3f408363bff3b to your computer and use it in GitHub Desktop.
Save lancegatlin/65d09d51d7b415eb94a3f408363bff3b to your computer and use it in GitHub Desktop.
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