for-comprehension with assignment 循环顺序错误
the for-comprehension with assignment loops in wrong order
给定 val l = List( List(0), List(1) )
for 循环:
for {
x <- l
_ = println(x)
y <- x
} {println(y)}
//将打印:
List(0)
List(1)
0
1
照片顺序错了!!
不就是翻译成下面这样吗?
for循环的翻译:
l.foreach(
x => {
println(x)
x.foreach(y => println(y))
}
)
List(0)
0
List(1)
1
---
我的问题:
- 为什么for循环不是按直觉顺序执行的? (我期待结果是使用 foreach 的第二个例子)
- 我的翻译有误吗?
- 为什么我们必须在 for-condition 部分赋值(例如
_ = print()
)? (只是一个 print()
将无法编译)
您给出的循环被翻译成以下内容:
l.map(((x) => {
val x = println(x);
scala.Tuple2(x, x)
})).foreach(((x) => x: @scala.unchecked match {
case scala.Tuple2((x @ _), (x @ _)) => x.foreach(((y) => println(y)))
}))
你可以在解释器中quasiquoting找到这样的东西。
发生的事情是赋值在之前与生成器相关联——就像你写for (x <- l; h = x.head)
一样,这两者的耦合是非常必要的。
如果你希望后续的每个生成器都发生副作用,你必须编写以下内容:
for {
x <- l
y <- {println(x); x}
} {println(y)}
这会产生您想要的打印输出,并且完全符合您的预期:
l.foreach(((x) => {
println(x);
x
}.foreach(((y) => println(y)))))
至于为什么必须显式丢弃生成器中的参数——有两个问题。首先,println
在不同的 monad 中。 运行 仅在 monad 理解中有效的表达式仅在同一个 monad 中才有意义,显然;这在列表 monad 中也不是很有用。如果您在假设的 IO monad 中工作,您将能够执行以下操作:
for {
y <- readLn()
_ <- printLn(x)
}
但是第二个问题来了:Scala 甚至不允许丢弃单元结果,我们仍然必须在 _
上进行模式匹配。
别问我为什么,就在the standard。恕我直言,允许这样做实际上是有意义的,就像 Haskell 所做的那样。但是,我能想到的一个原因就是您正在尝试做的事情:将一个 monad 的副作用与另一个 monad 混合。他们本可以一劳永逸地禁止这种做法。这也可能与这样一个事实有关,即在 Scala 中重写 for comprehensions 比在 Haskell 中更多地基于 duck typing,可能有更多的极端情况,甚至发生在类型检查之前(据我所知) ,这会使 "mixing" 复杂化很多。
这里的问题是你对for循环的翻译(这主要来自_ = println(x)
的错误翻译)。
从Scala Specs可以看到(第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') }
逐步应用转换,您得到:
for {
(x, u) <- for (x <- l) yield {val u = println(x); (x, u)}
y <- x
} {println(y)}
for {
(x,u) <- l.map {case x => val u = println(x); (x,u)}
y <- x
} {println(y)}
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => for ( y <- x ) {println(y)} }
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
和
scala> :paste
// Entering paste mode (ctrl-D to finish)
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
// Exiting paste mode, now interpreting.
List(0)
List(1)
0
1
给定 val l = List( List(0), List(1) )
for 循环:
for {
x <- l
_ = println(x)
y <- x
} {println(y)}
//将打印:
List(0)
List(1)
0
1
照片顺序错了!!
不就是翻译成下面这样吗? for循环的翻译:
l.foreach(
x => {
println(x)
x.foreach(y => println(y))
}
)
List(0)
0
List(1)
1
---
我的问题:
- 为什么for循环不是按直觉顺序执行的? (我期待结果是使用 foreach 的第二个例子)
- 我的翻译有误吗?
- 为什么我们必须在 for-condition 部分赋值(例如
_ = print()
)? (只是一个print()
将无法编译)
您给出的循环被翻译成以下内容:
l.map(((x) => {
val x = println(x);
scala.Tuple2(x, x)
})).foreach(((x) => x: @scala.unchecked match {
case scala.Tuple2((x @ _), (x @ _)) => x.foreach(((y) => println(y)))
}))
你可以在解释器中quasiquoting找到这样的东西。
发生的事情是赋值在之前与生成器相关联——就像你写for (x <- l; h = x.head)
一样,这两者的耦合是非常必要的。
如果你希望后续的每个生成器都发生副作用,你必须编写以下内容:
for {
x <- l
y <- {println(x); x}
} {println(y)}
这会产生您想要的打印输出,并且完全符合您的预期:
l.foreach(((x) => {
println(x);
x
}.foreach(((y) => println(y)))))
至于为什么必须显式丢弃生成器中的参数——有两个问题。首先,println
在不同的 monad 中。 运行 仅在 monad 理解中有效的表达式仅在同一个 monad 中才有意义,显然;这在列表 monad 中也不是很有用。如果您在假设的 IO monad 中工作,您将能够执行以下操作:
for {
y <- readLn()
_ <- printLn(x)
}
但是第二个问题来了:Scala 甚至不允许丢弃单元结果,我们仍然必须在 _
上进行模式匹配。
别问我为什么,就在the standard。恕我直言,允许这样做实际上是有意义的,就像 Haskell 所做的那样。但是,我能想到的一个原因就是您正在尝试做的事情:将一个 monad 的副作用与另一个 monad 混合。他们本可以一劳永逸地禁止这种做法。这也可能与这样一个事实有关,即在 Scala 中重写 for comprehensions 比在 Haskell 中更多地基于 duck typing,可能有更多的极端情况,甚至发生在类型检查之前(据我所知) ,这会使 "mixing" 复杂化很多。
这里的问题是你对for循环的翻译(这主要来自_ = println(x)
的错误翻译)。
从Scala Specs可以看到(第90页):
• A generator
p <- e
followed by a value definitionp' = e'
is translated to the following generator of pairs of values, wherex
andx'
are fresh names:
(p, p' ) <- for (x@p <- e) yield { val x'@p' = e' ; (x, x') }
逐步应用转换,您得到:
for {
(x, u) <- for (x <- l) yield {val u = println(x); (x, u)}
y <- x
} {println(y)}
for {
(x,u) <- l.map {case x => val u = println(x); (x,u)}
y <- x
} {println(y)}
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => for ( y <- x ) {println(y)} }
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
和
scala> :paste
// Entering paste mode (ctrl-D to finish)
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
// Exiting paste mode, now interpreting.
List(0)
List(1)
0
1