Created
February 22, 2011 16:59
-
-
Save krasserm/838976 to your computer and use it in GitHub Desktop.
Composition of concurrent functions that may fail
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 scalaz.concurrent._ | |
/** | |
* Result type of concurrent functions that may fail. | |
*/ | |
case class PromiseEither[A, B](value: Promise[Either[A, B]]) extends NewType[Promise[Either[A, B]]] | |
/** | |
* PromiseEither type class instances. | |
*/ | |
object PromiseEither { | |
/** | |
* Makes PromiseEither an instance of the Monad type class (needed for monadic composition). | |
*/ | |
implicit def PromiseEitherMonad[L](implicit s: Strategy) = | |
new Monad[({type λ[α]= PromiseEither[L, α]})#λ] { | |
def pure[A](a: => A) = PromiseEither(promise(a.right)) | |
def bind[A, B](a: PromiseEither[L, A], f: A => PromiseEither[L, B]): PromiseEither[L, B] = { | |
val pb = a.value.flatMap[Either[L, B]] { | |
case Left(l) => promise(l.left) | |
case Right(r) => f(r).value | |
} | |
PromiseEither(pb) | |
} | |
} | |
/** | |
* Makes PromiseEither an instance of the Apply type class (needed for applicative composition). | |
*/ | |
implicit def PromiseEitherApply[L]: Apply[({type λ[α]=PromiseEither[L, α]})#λ] = | |
FunctorBindApply[({type λ[α]=PromiseEither[L, α]})#λ] | |
} | |
object PromiseEitherExample { | |
/** | |
* Example concurrent function type. | |
*/ | |
type ExampleFunction = String => PromiseEither[String, String] | |
/** | |
* Kleisli type of ExampleFunction (needed for monadic composition) | |
*/ | |
type ExampleKleisli = Kleisli[({type λ[α] = PromiseEither[String, α]})#λ, String, String] | |
/** | |
* Implicit conversion from ExampleFunction to ExampleKleisli | |
*/ | |
implicit def toKleisli(f: ExampleFunction): ExampleKleisli = | |
kleisli[({type λ[α] = PromiseEither[String, α]})#λ, String, String](f) | |
def main(args: Array[String]) { | |
implicit val strategy = Strategy.Naive | |
// concurrent example function that appends a to s and succeeds | |
def append(a: String)(implicit s: Strategy): ExampleFunction = | |
(s: String) => PromiseEither(promise("%s%s".format(s, a).right)) | |
// concurrent example function that fails with input string s | |
def fail(implicit s: Strategy): ExampleFunction = | |
(s: String) => PromiseEither(promise(s.left)) | |
// monadic composition, all functions succeed | |
append("-1") >=> append("-2") apply "a" get match { | |
case Right(s) => assert(s == "a-1-2") | |
case _ => assert(false) | |
} | |
// monadic composition, second function fails | |
append("-1") >=> fail >=> append("-2") apply "a" get match { | |
case Left(s) => assert(s == "a-1") | |
case _ => assert(false) | |
} | |
// applicative composition (all functions succeed) | |
(append("-1") |@| append("-2")) { (e1, e2) => (e1 |@| e2) {_ + _} } apply "a" get match { | |
case Right(s) => assert(s == "a-1a-2") | |
case _ => assert(false) | |
} | |
// applicative composition (first function fails) | |
(fail |@| append("-2")) { (e1, e2) => (e1 |@| e2) {_ + _} } apply "a" get match { | |
case Left(s) => assert(s == "a") | |
case _ => assert(false) | |
} | |
// Note: applicative composition above can be further simplified using an EitherT | |
// monad transformer. EitherT would also easier to use in for-comprehensions compared | |
// to PromiseEither from this example. EitherT is not yet part of Scalaz 6.0-SNAPSHOT. | |
// See also http://groups.google.com/group/scalaz/browse_thread/thread/d062c793da6a6214 | |
// | |
// Combined applicative and monadic composition | |
// | |
// combinator of two concurrent functions (applicative composition) | |
def comb(f1: ExampleFunction, f2: ExampleFunction)(z: (String, String) => String): ExampleFunction = | |
(s: String) => PromiseEither((f1 |@| f2) { (e1, e2) => (e1 |@| e2)(z) } apply s) | |
// use (applicative) combinator as part of a monadic composition | |
append("-1") >=> comb(append("-x"), append("-y")) { | |
(s1, s2) => "(%s,%s)" format (s1, s2) | |
} >=> append(" done") apply "a" get match { | |
case Right(s) => assert(s == "(a-1-x,a-1-y) done") | |
case _ => assert(false) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment