【问题标题】:scala Either.RightProjection confusion (for comprehension de-sugaring)scala Either.RightProjection 混淆(用于理解去糖)
【发布时间】:2012-05-28 03:24:48
【问题描述】:

我可以在 scala 中使用= 进行理解(如6.19 of the SLS 部分所述),如下所示:

选项

假设我有一些函数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

我真正想做的事情使用与前面使用Either monad 的示例类似的理解

但是,如果我们在类似的链中使用它,但这次使用Either.RightProjection monad/functor,它就不起作用了:

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 =&gt; 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 comprehension 中?

【问题讨论】:

  • 小注:理解中不需要val 。只是j = i + 10 工作正常。但是,我没有理由不将计算放在yield 的右侧:} yield i + 10。相似之处:在第一个地图示例中,`map { j => j }` 没有任何作用,可以省略。
  • 我会像这样定义intOptintEitherdef intOpt(s: String) = allCatch opt s.toIntdef intEither(s: String) = allCatch either s.toInt(已经完成import scala.util.control.Exception._)。
  • @user unknown - 这只是一个例子;在我的用例中,我确实想在flatMaps 序列中嵌入一个map

标签: scala for-loop monads either


【解决方案1】:

你不能在理解中嵌入=这一事实与Jason Zaugg报告的this issue有关;解决方案是右偏Either(或创建一个与其同构的新数据类型)。

对于您的巨大困惑,您错误地扩展了糖。脱糖

for {
  b <- x(a)
  c <- y(b)
} yield z(c)

x(a) flatMap { b =>
 y(b) map { c =>
  z(c) }} 

而不是

x(a) flatMap { b => y(b)} map { c => z(c) }

因此你应该这样做:

scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)

更多关于脱糖的乐趣(`j = i + 10` 问题)

for {
  b <- x(a)
  c <- y(b)
  x1 = f1(b)
  x2 = f2(b, x1)
  ...
  xn = fn(.....)
  d <- z(c, xn)
} yield w(d)

脱糖成

x(a) flatMap { b =>
  y(b) map { c =>
    x1 = ..
    ...
    xn = ..
    (c, x1, .., xn) 
  } flatMap { (_c1, _x1, .., _xn) =>
    z(_c1, _xn) map w }}

因此,在您的情况下,y(b) 的结果类型 Either 没有定义 map

【讨论】:

  • 作为一种解决方法,我将右侧的理解中的“x = y”替换为“x
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-02
  • 1970-01-01
相关资源
最近更新 更多