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 a Left
.
You might imagine an implementation that works for your case of Either[String, Int]
, by failing with a default value of Left("")
. 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 to Either[A, B]
) which has a filter
method iff the left type is a monoid:
scala> \/.right[String, Int](1).filter(_ > 2)
res1: scalaz.\/[String,Int] = -\/()
But you couldn't do this for the general case - if you had an Either[Nothing, Int]
, you could never produce a left value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With