我可以在Scala的for循环推导中使用=
(如SLS第6.19节所指定的),示例如下:
选项
假设我有一个函数String => Option[Int]
:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
然后我可以这样使用它
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
我的理解是这与以下内容基本相当:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
也就是说,嵌入式生成器是一种将
map
注入到一系列 flatMap
调用中的方法。目前为止还不错。
Either.RightProjection
我想要做的实际上是:使用类似于前面例子中使用的for-comprehension,但是这次使用Either
单子类型。然而,如果我们在类似的链中使用
Either.RightProjection
单子/函子,它不起作用。scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
然后使用:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
这个问题与右投影所期望的作为其
flatMap
方法参数的函数有关(即它期望一个R => Either[L, R]
)。但是,即使修改第二个生成器以不调用right
,它仍无法编译。scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
巨大混乱
但是现在我变得更加困惑了。以下代码完全正常运行:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
但这个不会:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
我认为它们是等价的
- 发生了什么?
- 如何在跨越
Either
的 for 推导中嵌入一个=
生成器?
val
。只需使用j = i + 10
即可正常工作。但是,我认为没有理由不将计算放在yield
的右侧:} yield i + 10
。同样地,在第一个map示例中,map { j => j }
没有任何作用,可以省略。 - user unknownintOpt
和intEither
:def intOpt(s: String) = allCatch opt s.toInt
和def intEither(s: String) = allCatch either s.toInt
(在导入scala.util.control.Exception._
之后)。 - Jean-Philippe Pelletmap
嵌入到一系列的flatMap
s 中。 - oxbow_lakes