使用Scala的Eithers“为”语法(Using Eithers with Scala “for

2019-06-25 03:36发布

据我了解,斯卡拉“的”语法极其相似Haskell的单子“做”的语法。 在Scala中,“为”语法通常用于List S和Option秒。 我想用用它Either S,但必要的方法不存在默认进口。

for {
  foo <- Right(1)
  bar <- Left("nope")
} yield (foo + bar)

// expected result: Left("nope")
// instead I get "error: value flatMap is not a member..."

这是功能可通过一些进口?

有一个轻微的障碍:

for {
  foo <- Right(1)
  if foo > 3
} yield foo
// expected result: Left(???)

对于一个列表,这将是List() 对于Option ,这将是None 。 做斯卡拉标准库提供一个解决的办法? (或者scalaz ?)怎么样? 假设我想提供我自己的“单子实例”对于任何一个,我怎么能做到这一点?

Answer 1:

因为它没有在斯卡拉2.11和更早 Either是不是一个单子。 虽然有右偏置它说话,你不能在一个换理解使用它:你必须得到一个LeftProjectRightProjection ,如下图所示:

for {
  foo <- Right[String,Int](1).right
  bar <- Left[String,Int]("nope").right
} yield (foo + bar)

返回Left("nope")顺便说一句。

在Scalaz,你会替换EitherValidation 。 有趣的事实: Either的原始作者是托尼·莫里斯,Scalaz作者之一。 他想Either向右偏,但被同事否则信服。



Answer 2:

这是功能可通过一些进口?

是的,但通过第三方进口:Scalaz提供了一个Monad ,例如Either

import scalaz._, Scalaz._

scala> for {
     |   foo <- 1.right[String]
     |   bar <- "nope".left[Int]
     | } yield (foo.toString + bar)
res39: Either[String,java.lang.String] = Left(nope)

现在, if -Guard不是一个单子操作。 因此,如果您尝试使用if -Guard,它会导致编译器错误预期。

scala> for {
     |   foo <- 1.right[String]
     |   if foo > 3
     | } yield foo
<console>:18: error: value withFilter is not a member of Either[String,Int]
                foo <- 1.right[String]
                              ^

-上面所用的方便的方法.right.left -也从Scalaz。

编辑:

我错过了你的这个问题。

假设我想提供我自己的“单子实例”对于任何一个,我怎么能做到这一点?

斯卡拉for内涵是简单地转换为.map.flatMap.withFilter.filter .foreach所涉及的对象的调用。 (你可以找到完整的翻译方案在这里 。)所以,如果某些类没有所需的方法,可以将它们添加到使用隐式转换的类。

下方的新鲜REPL会话。

scala> implicit def eitherW[A, B](e: Either[A, B]) = new {
     |   def map[B1](f: B => B1) = e.right map f
     |   def flatMap[B1](f: B => Either[A, B1]) = e.right flatMap f
     | }
eitherW: [A, B](e: Either[A,B])java.lang.Object{def map[B1](f: B => B1): Product 
with Either[A,B1] with Serializable; def flatMap[B1](f: B => Either[A,B1]):
Either[A,B1]}

scala> for {
     |   foo <- Right(1): Either[String, Int]
     |   bar <- Left("nope") : Either[String, Int]
     | } yield (foo.toString + bar)
res0: Either[String,java.lang.String] = Left(nope)


Answer 3:

作为斯卡拉2.12, Either是现在右偏

从文档 :

如任一定义了方法映射和flatMap,它也可用于在用于推导:

 val right1: Right[Double, Int] = Right(1) val right2 = Right(2) val right3 = Right(3) val left23: Left[Double, Int] = Left(23.0) val left42 = Left(42.0) for ( a <- right1; b <- right2; c <- right3 ) yield a + b + c // Right(6) for ( a <- right1; b <- right2; c <- left23 ) yield a + b + c // Left(23.0) for ( a <- right1; b <- left23; c <- right2 ) yield a + b + c // Left(23.0) // It is advisable to provide the type of the “missing” value (especially the right value for `Left`) // as otherwise that type might be infered as `Nothing` without context: for ( a <- left23; b <- right1; c <- left42 // type at this position: Either[Double, Nothing] ) yield a + b + c // ^ // error: ambiguous reference to overloaded definition, // both method + in class Int of type (x: Char)Int // and method + in class Int of type (x: Byte)Int // match argument types (Nothing) 


文章来源: Using Eithers with Scala “for” syntax