Почему Scala выбирает тип "Продукт" для выражений "для", включающий определения "Либо" и "Значение"

Если я создаю для понимания с определением значения с помощью опции, он работает как ожидалось:

scala> for (a <- Some(4); b <- Some(5); val p = a * b) yield p
res0: Option[Int] = Some(20)

Выполнение того же самого действия с Либо работает, если у меня нет определения значения:

scala> for (a <- Right(4).right; b <- Right(5).right) yield a * b
res1: Either[Nothing,Int] = Right(20)

Но если я использовал определение значения, scala, похоже, выводит неверный тип контейнера для понимания:

scala> for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p
<console>:8: error: value map is not a member of Product with Serializable with Either[Nothing,(Int, Int)]
for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p
                            ^

Почему он это делает? Какими способами можно управлять этим поведением?

Ответы

Ответ 1

Проблемы возникают из val p = a*b Если вы напишете более простой

для (a < - Right (4).right; b < - Right (5).right) дают a * b

он компилируется, и вы получаете правильный результат.

У вашей проблемы две причины.

Во-первых, проекции Either map и flatMap не имеют обычной подписи, а именно для карт подпрограмм и flatMap, определенных в родовом классе M[A], (A => B) => M[B] и (A => M[B]) => M[B]. Подпрограмма M[A] определена в is Either[A,B].RightProjection, но в результатах и ​​аргументе мы имеем Either[A,B], а не проекцию.

Во-вторых, перевод val p = a*b в понимании. Scala Ссылка, 6.19 p 90:

Генератор p < - e, за которым следует определение значения p '= e', является переводится на следующий генератор пар значений, где x и x '- это новые имена:

(p,p′) <- for([email protected]<-e) yield {val x′@p′ = e′; (x,x′)}

Немного упростите код, отбросив a <-. Кроме того, b и p переименованы в p и pp, чтобы быть ближе к правилу перезаписи, с pp для p'. a должен находиться в зоне действия для (p < - Right (5).right; val pp = a * p) дают pp

Следуя правилу, мы должны заменить генератор + определение. Что вокруг, for( и )yield pp, без изменений.

for((p, pp) <- for([email protected] <- Right(5).right) yield{val [email protected] = a*p; (x,xx)}) yield pp

Внутреннее для переписывается на простое отображение

for((p, pp) <- Right(5).right.map{case [email protected] => val [email protected] = a*p; (x,xx)}) yield pp

Вот проблема. Right(5).right.map(...) имеет тип Either[Nothing, (Int,Int)], а не Either.RightProjection[Nothing, (Int,Int)], как мы хотели бы. Он не работает во внешнем for (который также преобразуется в map. В Either нет метода map, он определяется только для проекций.

Если вы внимательно посмотрите на свое сообщение об ошибке, оно говорит так, даже если он упоминает Product и Serializable, он говорит, что это Either[Nothing, (Int, Int)], и что на нем не определена карта. Пара (Int, Int) поступает непосредственно из правила перезаписи.

Понимание должно хорошо работать при соблюдении правильной подписи. С трюком с проекциями Either (который также имеет свои преимущества), мы получаем эту проблему.