使用带有 Scala“for”语法的任一种

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10866710/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:12:44  来源:igfitidea点击:

Using Eithers with Scala "for" syntax

scalamonadstypeclassfor-comprehensioneither

提问by Dan Burton

As I understand it, Scala "for" syntax is extremely similar to Haskell's monadic "do" syntax. In Scala, "for" syntax is often used for Lists and Options. I'd like to use it with Eithers, but the necessary methods are not present in the default imports.

据我了解,Scala 的“for”语法与 Haskell 的 monadic“do”语法极为相似。在 Scala 中,“for”语法经常用于Lists 和Options。我想将它与Eithers一起使用,但默认导入中不存在必要的方法。

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

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

Is this functionality available through some import?

此功能是否可以通过某些导入获得?

There is a slight hitch:

有一个小问题:

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

For a List, it would be List(). For Option, it would be None. Do the Scala standard libraries provide a solution to this? (Or perhaps scalaz?) How? Suppose I wanted to provide my own "monad instance" for Either, how could I do that?

对于列表,它将是List(). 对于Option,它会是None。Scala 标准库是否为此提供了解决方案?(或者也许scalaz?)如何?假设我想为Either 提供我自己的“monad 实例”,我该怎么做?

回答by Daniel C. Sobral

It doesn't work in scala 2.11 and earlierbecause Eitheris not a monad. Though there's talk of right-biasing it, you can't use it in a for-comprehension: you have to get a LeftProjector RightProjection, like below:

在 Scala 2.11 及更早版本中不起作用因为Either它不是 monad。尽管有人说它偏右,但你不能在理解中使用它:你必须得到一个LeftProjector RightProjection,如下所示:

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

That returns Left("nope"), by the way.

Left("nope")顺便说一下,那回来了。

On Scalaz, you'd replace Eitherwith Validation. Fun fact: Either's original author is Tony Morris, one of Scalaz authors. He wanted to make Eitherright-biased, but was convinced otherwise by a colleague.

在 Scalaz 上,您将替换EitherValidation. 有趣的事实:Either的原作者是托尼·莫里斯,Scalaz 的作者之一。他想让Either右偏,但被一位同事说服了。

回答by missingfaktor

Is this functionality available through some import?

此功能是否可以通过某些导入获得?

Yes, but via a third party import: Scalaz provides a Monadinstance for Either.

是的,但通过第三方导入:ScalazMonadEither.

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)

Now if-guard is not a monadic operation. Therefore if you attempt to use if-guard, it results in a compiler error as expected.

现在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]
                              ^

The convenience methods used above - .rightand .left- are also from Scalaz.

上面使用的方便方法 -.right.left- 也来自 Scalaz。

Edit:

编辑:

I missed this question of yours.

我错过了你的这个问题。

Suppose I wanted to provide my own "monad instance" for Either, how could I do that?

假设我想为Either 提供我自己的“monad 实例”,我该怎么做?

Scala forcomprehensions are simply translated to .map, .flatMap, .withFilter, and .filter.foreachcalls on the objects involved. (You can find the the full translation scheme here.) So if some class does not have the required methods, you can addthem to a class using implicit conversions.

Scala 推导式for简单地转换为.map, .flatMap, .withFilter, 并调用所涉及的对象。(您可以在此处找到完整的翻译方案。)因此,如果某些类没有所需的方法,您可以使用隐式转换它们添加到类中。.filter.foreach

A fresh REPL session below.

下面是一个新的 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)

回答by Matt Klein

As of Scala 2.12, Eitheris now right biased

从 Scala 2.12 开始,现在Either正确的

From the documentation:

文档

As Either defines the methods map and flatMap, it can also be used in for comprehensions:

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)

由于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)