簡體   English   中英

使用帶有Scala的Eithers“for”語法

[英]Using Eithers with Scala “for” syntax

據我了解,Scala“for”語法與Haskell的monadic“do”語法非常相似。 在Scala中,“for”語法通常用於ListOption 我想將它與Either一起Either ,但默認導入中不存在必要的方法。

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,它將是List() 對於Option ,它將是None Scala標准庫是否為此提供了解決方案? (或者也許scalaz ?)怎么樣? 假設我想為Either提供我自己的“monad實例”,我怎么能這樣做?

在scala 2.11及更早版本中不起作用因為Either不是monad。 雖然有關於權利LeftProject的討論,但你不能在for-comprehension中使用它:你必須得到一個LeftProjectRightProjection ,如下所示:

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

順便說一句,這會返回Left("nope")

在Scalaz上,您將使用Validation替換Either 有趣的事實: Either的原始作者是托尼·莫里斯,Scalaz作者之一。 他想讓Either做出正確的偏見,但同事卻說服了其他人。

這個功能是通過一些導入提供的嗎?

是的,但通過第三方進口: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不是一個monadic操作。 因此,如果您嘗試使用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。

編輯:

我錯過了你的這個問題。

假設我想為Either提供我自己的“monad實例”,我怎么能這樣做?

for理解的Scala簡單地轉換為.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)

從Scala 2.12開始, Either 現在都是正確的偏見

文檔

由於要么定義方法map和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) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM