Skip to content

Instantly share code, notes, and snippets.

@Fristi
Created December 9, 2015 21:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fristi/3884e656cc809f8d3102 to your computer and use it in GitHub Desktop.
Save Fristi/3884e656cc809f8d3102 to your computer and use it in GitHub Desktop.
EitherT + ReaderT monad transformer stack
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