Почему и Scala тоже.RightProjection#filter возвращает либо?

в Scala 2.9.1, рассмотрим следующие два случая 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)

это здорово, что, используя left и right проекции, можно использовать для-постижений (через пристрастную монаду проецируемого):

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)

может кто-нибудь объяснить мне, почему было принято решение не иметь filter метод возвращает либо? Я ожидал, что сработает следующее:

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)

вместо этого вы получаете следующую ошибку: :9: ошибка: значение * не является членом [Nothing, Int] для { r 2 } выход r * 2

кажется, что основной вызов в Either.RightProjection#filter на самом деле возвращает Option:

scala> er.right.filter(_ > 2)
res9: Option[Either[Nothing,Int]] = None

это побеждает использование предложения if в for-понимании, по крайней мере, так, как я пытался его использовать.

есть ли у кого-нибудь объяснение, почему этот дизайн таков, как он есть?

1 ответов


это сводится к тому, что если у вас есть Right(b), но ваш предикат фильтра терпит неудачу, у вас нет значения для ввода Left.

вы можете себе представить реализацию, которая работает для вашего случая Either[String, Int], если значение по умолчанию Left(""). Стандартная библиотека Scala не имеет возможности создать значение для вас, потому что она не включает в себя такое понятие, как моноид, который определил бы "пустое" значение для типа.

в Scalaz библиотека включает моноидный тип, а версия 7 также включает в себя тип дизъюнкции с правым уклоном,\/[A, B] (изоморфна Either[A, B]), который имеет filter метод iff левый тип является моноидом:

scala> \/.right[String, Int](1).filter(_ > 2)
res1: scalaz.\/[String,Int] = -\/()

но вы не могли бы сделать это для общего случая-если бы у вас был Either[Nothing, Int], вы никогда не сможете создать левое значение.