Skip to content

Instantly share code, notes, and snippets.

@stew
Last active December 18, 2015 08:19
Show Gist options
  • Save stew/5752733 to your computer and use it in GitHub Desktop.
Save stew/5752733 to your computer and use it in GitHub Desktop.

Revisions

  1. stew revised this gist Jun 10, 2013. 1 changed file with 33 additions and 12 deletions.
    45 changes: 33 additions & 12 deletions FSE.scala
    Original file line number Diff line number Diff line change
    @@ -7,19 +7,25 @@ import scala.concurrent.duration._
    import ExecutionContext.Implicits.global

    object FSE extends App {

    implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] with Zip[Future] = new Monad[Future] with Zip[Future] {
    // scalaz doesn't yet have a monad instance for future, here is one.
    // there is also one in the scalaz-contrib project
    implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] with Zip[Future] = new Monad[Future] {
    override def bind[A, B](fa: Future[A])(f: A Future[B]) = fa flatMap f
    override def point[A](a: A) = Future(a)
    override def map[A, B](fa: Future[A])(f: A B) = fa map f
    override def zip[A, B](fa: Future[A], fb: Future[B]): Future[(A, B)] = (fa |@| fb).tupled
    }

    // EitherT is a "Monad Transformer" which combines the effects \/ with some other monad
    // (In this case, Future)
    // EitherT wraps a type M[X \/ Y] so that the flatMap method combines both monads,
    // instead of having the flatMap from Future which takes a A => Future[B], we get a
    // flatMap which takes a A => Future[String \/ B]
    type FutureStringError[+A] = EitherT[Future,String,A]

    type FutureStringError[A] = EitherT[Future,String,A]



    /**
    Return "error".left if the string parses into an int or
    Return int.right if there is a successful parse
    */
    def parseInt(x: String): FutureStringError[Int] = {
    EitherT { Future {
    try {
    @@ -30,6 +36,10 @@ object FSE extends App {
    } }
    }

    /**
    Divide numerator by divisor, and return the result as int.right,
    return "error".left on divide by zero
    */
    def divide(numerator: Int)(divisor: Int): FutureStringError[Int] = {
    EitherT { Future {
    if(divisor == 0)
    @@ -39,19 +49,30 @@ object FSE extends App {
    } }
    }

    def calculate(str: String): FutureStringError[Int] =
    /** combine the operations monadically, combining the effects of both Future and \/ */
    def calculate(str: String): FutureStringError[Int] =
    for {
    divisor <- parseInt(str)
    divided <- divide(100)(divisor)
    } yield(divided)


    /** extra bonus, since the functions we combine above, both happen to be of the shape, A => M[B], we can combine them using kleisli composition
    */
    val calculate2: String => FutureStringError[Int] =
    (Kleisli(parseInt _) >=> Kleisli(divide(100))).run

    def printSuccess(f: Future[Any]) = {
    f onSuccess { case a => println(a) }
    Await.result(f, 1 second)
    }

    printSuccess(calculate("10").run)
    printSuccess(calculate("asdf").run)
    printSuccess(calculate("0").run)
    }
    printSuccess(calculate("10").run) // \/-(10)
    printSuccess(calculate("asdf").run) // -\/(For input string: "asdf")
    printSuccess(calculate("0").run) // -\/(cannot divide by zero)

    // These are the same as above
    printSuccess(calculate2("10").run)
    printSuccess(calculate2("asdf").run)
    printSuccess(calculate2("0").run)
    }
  2. stew created this gist Jun 10, 2013.
    57 changes: 57 additions & 0 deletions FSE.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    import scalaz._
    import Scalaz._

    import scala.concurrent.{ExecutionContext, Future}
    import scala.concurrent.Await
    import scala.concurrent.duration._
    import ExecutionContext.Implicits.global

    object FSE extends App {

    implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] with Zip[Future] = new Monad[Future] with Zip[Future] {
    override def bind[A, B](fa: Future[A])(f: A Future[B]) = fa flatMap f
    override def point[A](a: A) = Future(a)
    override def map[A, B](fa: Future[A])(f: A B) = fa map f
    override def zip[A, B](fa: Future[A], fb: Future[B]): Future[(A, B)] = (fa |@| fb).tupled
    }


    type FutureStringError[A] = EitherT[Future,String,A]



    def parseInt(x: String): FutureStringError[Int] = {
    EitherT { Future {
    try {
    Integer.parseInt(x).right
    } catch {
    case e: Throwable => e.getMessage.left
    }
    } }
    }

    def divide(numerator: Int)(divisor: Int): FutureStringError[Int] = {
    EitherT { Future {
    if(divisor == 0)
    "cannot divide by zero".left
    else
    (numerator / divisor).right
    } }
    }

    def calculate(str: String): FutureStringError[Int] =
    for {
    divisor <- parseInt(str)
    divided <- divide(100)(divisor)
    } yield(divided)

    def printSuccess(f: Future[Any]) = {
    f onSuccess { case a => println(a) }
    Await.result(f, 1 second)
    }

    printSuccess(calculate("10").run)
    printSuccess(calculate("asdf").run)
    printSuccess(calculate("0").run)
    }