为什么Scala选择“Product”类型?为了“为了”涉及 Either 和值定义的表达式

发布于 2024-12-03 06:15:55 字数 758 浏览 0 评论 0原文

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

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

如果我没有值定义,则使用 E​​ither 做同样的事情会起作用:

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
                            ^

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

If I create a for comprehension with a value definition with Option, it works as expected:

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

Doing the same thing with Either works if i have no value definition:

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

But if i used the value definition, scala seems to infer the wrong container type for the 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
                            ^

Why does it do this? What ways around this behavior are available?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦与时光遇 2024-12-10 06:15:55

问题来自 val p = a*b
如果您编写更简单的

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

它会编译并获得正确的结果。

您的问题有两个原因

首先, Either 投影 mapflatMap 没有通常的签名,即对于在 a 中定义的例程 map 和 flatMap泛型类 M[A], (A => B) => M[B](A => M[B]) => M[B]。例程中定义的 M[A]Either[A,B].RightProjection,但在结果和参数中,我们有 Either[A,B] ] 而不是投影。

其次,for 理解中 val p = a*b 的翻译方式。 Scala 参考,6.19 第 90 页:

生成器 p <- e 后跟值定义 p′ = e′ 是
转换为以下值对生成器,其中 x 和
x′ 是新名称:

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

让我们稍微简化一下代码,删除 a <-。此外,将 bp 重命名为 ppp 以更接近重写规则,其中 pp 代表 p'a 应该在范围内
for(p <- Right(5).right; val pp = a*p) 产生 pp

遵循规则,我们必须替换生成器 + 定义。周围是什么,for( and )yield pp,没有改变。

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

内部的for被重写为一个简单的map,

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

这就是问题所在。 Right(5).right.map(...) 的类型为 Either[Nothing, (Int,Int)],而不是 Either.RightProjection[没有任何东西,(Int,Int)] 正如我们所希望的那样。它在外部 for 中不起作用(它也转换为 mapEither 上没有 map 方法,它是在投影上定义的如果

您仔细查看错误消息,它会这样说,即使它提到 ProductSerialized,它也会说它是 Either[Nothing, (国际, Int)],并且没有在其上定义映射对 (Int, Int) 直接来自重写规则。

for 理解旨在在尊重正确的情况下正常工作 。通过使用 Either 投影(也有其优点)的技巧,我们得到了这个问题。

The problems comes from val p = a*b
If you write the simpler

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

it compiles and you get the proper result.

Your problem has two causes

First, the Either projections map and flatMap do not have the usual signature , namely for routines map and flatMap defined in a generic class M[A], (A => B) => M[B] and (A => M[B]) => M[B]. The M[A] the routine are defined in is Either[A,B].RightProjection, but in results and argument, we have Either[A,B] and not the projection.

Second, the way val p = a*b in the for comprehension is translated. Scala Reference, 6.19 p 90:

A generator p <- e followed by a value definition p′ = e′ is
translated to the following generator of pairs of values, where x and
x′ are fresh names:

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

Let's simplify the code just a little bit, dropping the a <-. Also, b and p renamed to p and pp to be closer to the rewrite rule, with pp for p'. a supposed to be in scope
for(p <- Right(5).right; val pp = a*p) yield pp

following the rule, we have to replace the generator + definition. What is around that, for( and )yield pp, unchanged.

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

The inner for is rewritten to a simple map

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

Here is the problem. The Right(5).right.map(...) is of type Either[Nothing, (Int,Int)], not Either.RightProjection[Nothing, (Int,Int)] as we would want. It does not work in the outer for (which converts to a map too. There is no map method on Either, it is defined on projections only.

If you look closely at your error message, it says so, even if it mentions Product and Serializable, it says that it is an Either[Nothing, (Int, Int)], and that no map is defined on it. The pair (Int, Int) comes directly from the rewrite rule.

The for comprehension is intended to work well when respecting the proper signature. With the trick with Either projections (which has its advantages too), we get this problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文