Created
December 9, 2015 21:05
-
-
Save Fristi/3884e656cc809f8d3102 to your computer and use it in GitHub Desktop.
EitherT + ReaderT monad transformer stack
This file contains hidden or 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 nl.mdj | |
import scala.concurrent.duration._ | |
import scala.concurrent.{Await, Future} | |
import scala.util.{Failure, Success, Try} | |
import scalaz.Scalaz._ | |
import scalaz._ | |
object Transformers extends App { | |
implicit val executionContext = scala.concurrent.ExecutionContext.global | |
case class Env(hostname: String) | |
sealed trait Error | |
object Error { | |
case object CannotDivideByZero extends Error | |
final case class ExceptionOccurred(ex: Throwable) extends Error | |
} | |
type Task[R] = ReaderT[Future, Env, R] | |
type Result[A] = EitherT[Task, Error, A] | |
object Result { | |
implicit def taskMonad(implicit M: Monad[Future]): MonadReader[ReaderT[Future, Transformers.Env, ?], Env] = | |
ReaderT.kleisliMonadReader[Future, Env] | |
implicit def resultMonad(implicit M: Monad[Task]): Monad[DisjunctionT[Transformers.Task, Transformers.Error, ?]] = | |
EitherT.eitherTMonad[Task, Error] | |
def async[R](statement: Env => Future[Error \/ R]): Result[R] = | |
EitherT[Task, Error, R](Kleisli(f => statement(f))) | |
def sync[R](statement: Env => Error \/ R): Result[R] = | |
async(env => Future.successful(statement(env))) | |
def point[R](e: => R): Result[R] = | |
Monad[Result].point(e) | |
def require(test: => Boolean, failure: Error): Result[Unit] = | |
sync[Unit](_ => if(test) \/.right(()) else \/.left(failure)) | |
def fromTry[R](f: Env => Try[R]): Result[R] = sync { env => | |
f(env) match { | |
case Success(res) => \/.right(res) | |
case Failure(ex) => \/.left(Error.ExceptionOccurred(ex)) | |
} | |
} | |
def fromOption[R](notFound: => Error)(opt: Option[R]): Result[R] = | |
sync(_ => opt.fold[Error \/ R](\/.left(notFound))(\/.right)) | |
def fromEither[EE, R](f: EE => Error)(either: Either[EE, R]) = | |
sync(_ => either.fold(ee => \/.left(f(ee)), \/.right)) | |
} | |
def divide(x: Int, y: Int) = for { | |
_ <- Result.require(y > 0, Error.CannotDivideByZero) | |
} yield x / y | |
val r = (for { | |
div <- Result.point(3) | |
res <- divide(9, div) | |
} yield res).run(Env("localhost")) | |
println(Await.result(r, 1.second)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment