package com.milessabin.chain object Chain { import scala.reflect.Manifest abstract sealed case class Chain[+A, +X]() { def map[B](f : A => B) : Chain[B, X] def flatMap[B, Y >: X](f : A => Chain[B, Y]) : Option[Either[Y, B]] def filter(p : A => Boolean) : Chain[A, X] = this def foreach(b : A => Unit) {} } private case object Empty extends Chain[Nothing, Nothing] { override def map[B](f : Nothing => B) = this override def flatMap[B, Y >: Nothing](f : Nothing => Chain[B, Y]) = None } private case class Failure[X](x : X) extends Chain[Nothing, X] { override def map[B](f : Nothing => B) = this override def flatMap[B, Y >: X](f : Nothing => Chain[B, Y]) = Some(Left(x)) } private case class Success[A, X](v : A) extends Chain[A, X] { override def map[B](f : A => B) = Success(f(v)) override def flatMap[B, Y >: X](f : A => Chain[B, Y]) : Option[Either[Y, B]] = f(v) match { case Empty => None case Success(v2) => Some(Right(v2)) case Failure(x) => Some(Left(x)) } override def filter(p : A => Boolean) = if(p(v)) this else Empty override def foreach(b : A => Unit) = b(v) } implicit def option2ChainBuilder[A](o : Option[A]) = new { def ||[X](x : => X) = o match { case Some(v) => Success(v) case None => Failure(x) } def ||[X](x : None.type) = o match { case Some(v) => Success(v) case None => Empty } } implicit def chain2Iterable[A, X](c: Chain[A, X]): Iterable[Either[X, A]] = c match { case Empty => Nil case Success(v) => List(Right(v)) case Failure(x) => List(Left(x)) } implicit def chain2Option[A, X](c : Chain[A, X]): Option[Either[X, A]] = c match { case Empty => None case Success(v) => Some(Right(v)) case Failure(x) => Some(Left(x)) } def nonNull[T](x : T) = if (x != null) Some(x) else None def as[T](x : Any)(implicit m : Manifest[T]) = if (m.erasure.isInstance(x)) Some(x.asInstanceOf[T]) else None } object TestChain extends Application { import Chain._ abstract sealed case class Err() case class ErrNull(msg : String) extends Err case class ErrNotString(msg : String) extends Err //val foo : Any = null //val foo : Any = 1 val foo : Any = "Hello world" val flag = true val v = for { a <- nonNull(foo) || ErrNull("foo is null") b <- as[String](a) || ErrNotString("foo isn't a String") if flag } yield b println(v) val foos : List[Any] = List("Hello world", null, 1, "Goodbye cruel world") val flag2 = true val vs = for { a <- foos b <- nonNull(a) || ErrNull("This element of foos is null") c <- as[String](b) || ErrNotString("This element of foos isn't a String") if flag2 } yield c println(vs) val mvs = vs.map(Either.merge[AnyRef]) println(mvs) }