【问题标题】:Scala's Either not Having `flatMap` & Meaning of Either.left/.rightScala 的 Either 没有 `flatMap` 和 Either.left/.right 的含义
【发布时间】:2020-04-16 14:37:08
【问题描述】:

查看 Haskell 的 Either Monad,有一个 >>= 函数。

Prelude Map> let add100 = \x -> Right (x+100 :: Int)

Prelude Map> x
Right 5
Prelude Map> x >>= add100
Right 105

Prelude Map> let y = Left "..." :: Either String Int
Prelude Map> y >>= add100
Left "..."

但是,为什么 Scala 的 Either[A,B] 没有 flatMap,即等效于 >>= 函数?

scala> e
res5: Either[String,Int] = Right(1)

scala> e.
asInstanceOf   fold           isInstanceOf   isLeft         isRight
joinLeft       joinRight      left           right          swap
toString

另外,leftright是什么意思?

scala> e.left
res6: scala.util.Either.LeftProjection[String,Int] = LeftProjection(Right(1))

scala> e.right
res7: scala.util.Either.RightProjection[String,Int] = RightProjection(Right(1))

【问题讨论】:

    标签: scala haskell


    【解决方案1】:

    您可以使用fold,它需要两个功能,一左一右,或使用right 投影视图:

    val e: Either[String, Int] = Right(5)
    
    def add100(i: Int): Either[String, Int] = Right(i + 100)
    
    e.fold(identity, add100)  // Right(105)
    e.right.flatMap(add100)   // Right(105)
    

    因此,投影视图允许您将Either 实例视为将通过左或右类型映射的东西。这在Eitherscala-doc help file中有解释。


    如果您熟悉 Haskell,ScalaZ 库可能适合您,它有自己的“任意”抽象,称为 \/(如 here 所述)。

    【讨论】:

    • 谢谢。但是为什么 Either 没有 flatMap 作为方法呢?
    • @KevinMeredith 查看最后一个链接的博客。 scala.util.Either 是公正的,因此不是单子本身,仅通过投影。如果你想要一个带有直接单子接口的右偏变体,Scalaz 适合你,或者(参见@LimbSoup 的答案)使用Try为什么它是这样设计的,我不知道。
    • 此答案仅对 Scala 版本 stackoverflow.com/a/61244126/2219550 关于更新版本的 Scala 其中 Either 支持 flatMap
    【解决方案2】:

    Either 不能有flatMap,因为Either 不是monad。 Haskell 通过偏爱这两种类型中的一种使其成为 monad,但实际上没有理由这样做。 Either 表示“这可以是两种类型之一”。时期。它不会对这两种类型中的一种给予任何优惠。

    如果您使用Either 进行错误报告,那么您确实希望它偏向一侧,但这只是众多用例中的一个,并且将单个特殊用例硬编码到通用界面中闻起来很糟糕设计。对于那个用例,你也可以使用Try,这基本上是一个有偏见的Either

    leftright 返回Either 的投影, 偏向一侧。

    但是,请注意Either 的设计和可用性一直备受争议,它确实不是良好 API 设计的闪亮灯塔。这也是 Try 和 ScalaZ \/ 存在的原因之一。

    【讨论】:

    • Haskell makes it into a monad by favoring one of the two types over the other。当您说favor 时,是否意味着假设Right 是成功而Left 表示失败?
    • @KevinMeredith: "favor" 意味着return 将只返回右侧,>>= 将简单地绑定右侧并忽略左侧或将其通过不变。这意味着右侧比左侧更受青睐,我们通常说它是“右偏”。但是没有真正的理由这样做,除非您使用Either 表示错误并且您认为Right 是成功的。但是Either还有其他用途,即使你把它用于错误,那么把错误放在右边和成功放在左边同样有效。
    【解决方案3】:

    Scala 的 Either 类型是无偏见的。对于 TryFuture 等类型,mapflatMap 在成功状态下运行,但对于 Either,情况并非如此(尽管 Right 通常被视为成功)。如果Either 没有真正的成功状态,那么flatMap 应该操作哪个值? Left 还是 Right?这取决于您的预测。

    leftrightEither 的投影,它们本质上是Either 的偏差版本。

    val test: Either[String, Int] = Right(1)
    
    test.right.map(_ + 1) // Right-biased, will map the value to Right(2)
    
    test.left.map(_ + 1)  // Left-biased, will not operate on the value since it's `Right`, therefore returning Right(1) again.
    

    【讨论】:

    • 而这些右/左投影使事情变得更糟,因为它们令人困惑地具有名为“map”和“flatMap”的方法,它们具有与您期望的相似但不同的签名。通常,map 是我们从 Functor 知道的映射,即 F[A] => (A => B) => F[B],但这里是 RightProduction[A] => (A => B) => Either[A] (为什么不是返回类型 RightProjection[B]) ?同样,flatMap 的返回类型是 Either[B] 而不是 RightProjection[B]。呸
    【解决方案4】:

    Scala 的 Either 类在 Scala 2.12 中进行了重新设计。在 2.12 之前,Either 没有偏见,也没有实现 map 和 flatMap 方法。正如书中的图片所示,Either 在 2.12 中经过重新设计以包含这些方法,因此它现在可以在 Scala for 表达式中使用,如图所示。

    https://alvinalexander.com/photos/scala-either-left-right-2.12-biased-map-flatmap/

    在 Scala 2.12 中,您可以例如这个:

    for {
     right1 <- either1
     right2 <- either2
    } yield (righ1 + right2)
    

    【讨论】:

      猜你喜欢
      • 2015-01-05
      • 1970-01-01
      • 2021-01-18
      • 2019-02-09
      • 2017-04-23
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 2023-03-22
      相关资源
      最近更新 更多