Last active
December 18, 2015 08:19
-
-
Save stew/5752733 to your computer and use it in GitHub Desktop.
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
| 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 { | |
| // 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 { | |
| Integer.parseInt(x).right | |
| } catch { | |
| case e: Throwable => e.getMessage.left | |
| } | |
| } } | |
| } | |
| /** | |
| 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) | |
| "cannot divide by zero".left | |
| else | |
| (numerator / divisor).right | |
| } } | |
| } | |
| /** 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) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment