Skip to content

Instantly share code, notes, and snippets.

@krasserm
Created February 22, 2011 16:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krasserm/838976 to your computer and use it in GitHub Desktop.
Save krasserm/838976 to your computer and use it in GitHub Desktop.
Composition of concurrent functions that may fail
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