Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Scala's Either.RightProjection#filter return an Either?

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?

like image 988
ms-tg Avatar asked Feb 07 '13 18:02

ms-tg


1 Answers

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.

like image 90
Ben James Avatar answered Nov 15 '22 11:11

Ben James