Last active
December 18, 2015 08:19
-
-
Save stew/5752733 to your computer and use it in GitHub Desktop.
Revisions
-
stew revised this gist
Jun 10, 2013 . 1 changed file with 33 additions and 12 deletions.There are no files selected for viewing
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 charactersOriginal 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 { // 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 } // 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] /** 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 { } } } /** 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) // \/-(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) } -
stew created this gist
Jun 10, 2013 .There are no files selected for viewing
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 charactersOriginal 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) }