Using Scala 2.9.1, consider the following two instances of Either:
scala> val er: Either[String, Int] = Right(1)
er: Either[String,Int] = Right(1)
scala> val el: Either[String, Int] = Left("a")
el: Either[String,Int] = Left(a)
It’s great that, by using the left and right projections, one can use for-comprehensions (via biased monad of the projected Either):
scala> for { r <- er.right } yield r * 2
res6: Product with Either[String,Int] with Serializable = Right(2)
scala> for { r <- el.right } yield r * 2
res7: Product with Either[String,Int] with Serializable = Left(a)
Can someone explain to me why the decision was made not to have the filter method return an Either? I would have expected the following to work:
scala> for { r <- er.right if r > 2 } yield r * 2 // r is NOT greater than 2!
res8: Product with Either[String,Int] with Serializable = Left(a)
Instead you get the following error:
:9: error: value * is not a member of Either[Nothing,Int]
for { r <- er.right if r > 2 } yield r * 2
It appears that the underlying call in Either.RightProjection#filter actually returns an Option:
scala> er.right.filter(_ > 2)
res9: Option[Either[Nothing,Int]] = None
This defeats the use of the if clause in the for-comprehension, at least the way I was trying to use it.
Does anyone have an explanation of why this design is the way it is?
It boils down to the fact that if you have a
Right(b), but your filter predicate fails, you have no value to put in aLeft.You might imagine an implementation that works for your case of
Either[String, Int], by failing with a default value ofLeft(""). The Scala standard library doesn’t have the facility to to produce a value for you, because it doesn’t include a concept such as a monoid which would determine the “empty” value for a type.The Scalaz library does include a monoid typeclass, and version 7 also includes a right-biased disjunction type,
\/[A, B](isomorphic toEither[A, B]) which has afiltermethod iff the left type is a monoid:But you couldn’t do this for the general case – if you had an
Either[Nothing, Int], you could never produce a left value.