为什么Scala为涉及Either和值定义的'for'表达式选择"Product"类型

srp*_*ish 14 scala type-inference for-comprehension

如果我使用Option创建一个带有值定义的for comprehension,它将按预期工作:

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

如果我没有值定义,则使用Either做同样的事情:

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

但是如果我使用了值定义,scala似乎推断了for comprehension的错误容器类型:

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
                            ^
Run Code Online (Sandbox Code Playgroud)

为什么这样做?有哪些方法可以解决这种问题?

Did*_*ont 19

问题来自于val p = a*b 如果你写得更简单

for(a < - Right(4).right; b < - Right(5).right)产生a*b

它编译,你得到了正确的结果.

你的问题有两个原因

首先,Either投影mapflatMap没有通常的签名,即用于在泛型类中定义的例程map和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(x@p<-e) yield {val x?@p? = e?; (x,x?)}
Run Code Online (Sandbox Code Playgroud)

让我们简单地简化一下代码吧a <-.此外,bp更名为ppp更接近重写规则,以ppp'.a应该在(p < - Right(5).right; val pp = a*p)yield pp的范围内

遵循规则,我们必须替换生成器+定义.这是什么,for(并且)yield pp没有改变.

for((p, pp) <- for(x@p <- Right(5).right) yield{val xx@pp = a*p; (x,xx)}) yield pp
Run Code Online (Sandbox Code Playgroud)

内部for被重写为一个简单的地图

for((p, pp) <- Right(5).right.map{case x@p => val xx@pp = a*p; (x,xx)}) yield pp
Run Code Online (Sandbox Code Playgroud)

这是问题所在.该Right(5).right.map(...)类型是Either[Nothing, (Int,Int)],不是Either.RightProjection[Nothing, (Int,Int)]因为我们想.它在外部for(也转换为a)中不起作用map.没有map方法Either,它仅在投影上定义.

如果您在您的错误信息仔细一看,它是这么说的,即使它提到ProductSerializable,它说,它是一个Either[Nothing, (Int, Int)],而没有地图上定义.该对(Int, Int)直接来自重写规则.

理解是为了在尊重适当的签名时运作良好.通过Either投影技巧(也有其优势),我们遇到了这个问题.