Both cchantep and Marth are good solutions to your immediate problem. But in a broader sense, it is difficult to consider it as something completely analogous to Option , in particular, allowing you to express sequences of potentially unsuccessful calculations for understanding. It either has a projection API (used in the cchantep solution), but it is a bit broken. (Either projections are built for understanding with protection, pattern matching, or variable assignment.)
FWIW, I wrote a library to solve this problem. It increases either this API . You define bias for your Aithers. “Right offset” means that a regular stream (map, get, etc.) is represented by a Right object, and Left objects are some kind of problem. (The right offset is normal, although you can also define the left offset if you want). Then you can consider Either as Option ; it disables a completely similar API.
import com.mchange.leftright.BiasedEither import BiasedEither.RightBias._ val myEither:Either[String, Object] = ... val o = myEither.getOrElse( "Substitute" )
More useful, now you can treat either as a real scala monad, i.e. use flatMap, map, filter and for understanding:
val myEither : Either[String, Point] = ??? val nextEither = myEither.map( _.x )
or
val myEither : Either[String, Point] = ??? def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ??? val locPopPair : Either[String, (Point, Long)] = { for { p <- myEither g <- findGalaxyAtPoint( p ) } yield { (p, g.population) } }
If all processing steps are successful, locPopPair will be Right[Long] . If something went wrong, this will be the first Left[String] encountered.
This is a little trickier, but it's nice to define an empty token. Let's look at a small change for understanding above:
val locPopPair : Either[String, (Point, Long)] = { for { p <- myEither g <- findGalaxyAtPoint( p ) if px > 1000 } yield { (p, g.population) } }
What happens if the px > 1000 test fails? We want to return some Left , which means "empty", but a universal suitable value (not all Left are Left[String] ). At this point, what happens, the code will throw a NoSuchElementException but we can specify an empty token ourselves, as shown below:
import com.mchange.leftright.BiasedEither val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY") import RightBias._ val myEither : Either[String, Point] = ??? def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ??? val locPopPair : Either[String, (Point, Long)] = { for { p <- myEither g <- findGalaxyAtPoint( p ) if px > 1000 } yield { (p, g.population) } }
Now, if the px > 1000 test fails, there will be no exceptions, locPopPair will just be Left("EMPTY") .