正确的投影混淆(理解脱糖) [英] scala Either.RightProjection confusion (for comprehension de-sugaring)

查看:181
本文介绍了正确的投影混淆(理解脱糖)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以在scala for-comprehension中使用 = (如 6.19 的SLS )如下所示:

h2>

假设我有一些函数 String =>选项[Int]

  scala> def intOpt(s:String)= try {Some(s.toInt)} catch {case _ =>无} 
intOpt:(s:String)选项[Int]

然后我可以使用它因此

  scala>为{
| str< - Option(1)
|我< - intOpt(str)
| val j = i + 10 //注意在生成器
|中使用= }
|产量j
res18:Option [Int] =一些(11)

这是我的理解这基本上相当于:

  scala>选项(1)flatMap {str => intOpt(str)} map {i => i + 10} map {j => j} 
res19:Option [Int] =一些(11)

也就是说,嵌入式生成器是一种将 map 注入到 flatMap 调用序列中的方法。到目前为止这么好。



或者.RightProjection



我真正想做的事使用与前面使用任一 monad 的示例类似的理解。

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

  scala> def intEither(s:String):或者[Throwable,Int] = 
|尝试{Right(s.toInt)} catch {case x => Left(x)}
intEither:(s:String)[Throwable,Int]

然后使用:

  scala>为{
| str< - Option(1)。toRight(new Throwable())right
|我< - intEither(str).right //注意右投影使用
| val j = i + 10
| }
| (java.lang.Throwable,(Int,Int)]
i< - intEither(str);返回值为j
< console>:17:error:value map不是Product的一个成员, ).right
^

这个问题与这个函数有关,投影期望作为其 flatMap 方法的一个参数(即它期望一个 R => [L,R] )。但修改为不在第二个生成器上调用 right ,它仍然不会编译。

 阶>为{
| str< - Option(1)。toRight(new Throwable())right
|我< - intEither(str)//否rightprojection
| val j = i + 10
| }
| yield j
< console>:17:error:value map不是Either [Throwable,Int]
i < - intEither(str)
^
的成员。 code>



超级混乱



但现在我倍感困惑。下面的例子很好:

  scala>为{
| x< - 右[Throwable,String](1)。右
| y< - 右[Throwable,String](x).right //注意这里的right
| } yield y.toInt
res39:或者[Throwable,Int] = Right(1)

但是这不是:

  scala> Right [Throwable,String](1)。right flatMap {x =>右[Throwable,String](x).right} map {y => y.toInt} 
< console>:14:error:type mismatch;
found:Either.RightProjection [Throwable,String]
required:[?,?]
Right [Throwable,String](1)。right flatMap {x =>右[Throwable,String](x).right} map {y => y.toInt}
^

我认为这些是相同的


  • 发生了什么事情?

  • 如何嵌入 = >


解析方案 div>

不能将 = 嵌入到for-comprehension中的事实与这个问题由Jason Zaugg报道;解决的办法是右偏或者(或者创建一个与它同构的新数据类型)。



巨大的困惑,你不正确地扩大了糖。 (
b < - x(a)
c < - y(b)的解除/ b
$ 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 =>右[Throwable,String](x).right map {y => y.toInt}} 
res49:[Throwable,Int] = Right(1)



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



 对于{
b < - x (a)
c <-y(b)
x1 = f1(b)
x2 = f2(b,x1)
...
xn = fn (.....)
d <-z(c,xn)
}得到w(d)

被解析成

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

$ c> y(b)的结果类型为或者不包含 map 定义。

I can use an = in a scala for-comprehension (as specified in section 6.19 of the SLS) as follows:

Option

Suppose I have some function String => Option[Int]:

scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]

Then I can use it thus

scala> for {
   |     str <- Option("1")
   |     i <- intOpt(str)
   |     val j = i + 10    //Note use of = in generator
   |   }
   |   yield j
res18: Option[Int] = Some(11)

It was my understanding that this was essentially equivalent to:

scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)

That is, the embedded generator was a way of injecting a map into a sequence of flatMap calls. So far so good.

Either.RightProjection

What I actually want to do: use a similar for-comprehension as the previous example using the Either monad.

However, if we use it in a similar chain, but this time using the Either.RightProjection monad/functor, it doesn't work:

scala> def intEither(s: String): Either[Throwable, Int] = 
  |      try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]

Then use:

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
                ^

The issue has something to do with the function that a right-projection expects as an argument to its flatMap method (i.e. it expects an R => Either[L, R]). But modifying to not call right on the second generator, it still won't compile.

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)
                            ^

Mega-Confusion

But now I get doubly confused. The following works just fine:

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)

But this does not:

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 }
                                                                                             ^

I thought these were equivalent

  • What is going on?
  • How can I embed an = generator in a for comprehension across an Either?

解决方案

The fact that you cannot embed the = in the for-comprehension is related to this issue reported by Jason Zaugg; the solution is to Right-bias Either (or create a new data type isomorphic to it).

For your mega-confusion, you expanded the for sugar incorrectly. The desugaring of

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

is

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

and not

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

Hence you should have done this:

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

More fun about desugaring (the `j = i + 10` issue)

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

is desugared into

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

So in your case, y(b) has result type Either which doesn't have map defined.

这篇关于正确的投影混淆(理解脱糖)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆