web-dev-qa-db-ja.com

Scala "for"構文でEithersを使用する

私が理解しているように、Scala "for"構文はHaskellのモナディック "do"構文に非常に似ています。Scalaでは、 "["構文は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..."
_

この機能はインポートによって利用できますか?

わずかな問題があります。

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

リストの場合、List()になります。 Optionの場合、Noneになります。 Scala標準ライブラリがこれに対する解決策を提供しますか?(またはおそらくscalaz?)どのように?独自の "モナドインスタンス"を提供したいと思います。それを行う?

40
Dan Burton

動作しませんin scala 2.11以前Eitherはモナドではないためです。右バイアスの話はありますが、 t内包表記で使用します。以下のように、LeftProjectまたはRightProjectionを取得する必要があります。

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

ところで、これはLeft("nope")を返します。

Scalazでは、EitherValidationに置き換えます。面白い事実:Eitherの元の作者は、Scalaz作者の1人であるTony Morrisです。彼はEitherを右バイアスにすることを望んでいましたが、それ以外の場合は同僚によって確信されました。

54

この機能はインポートによって利用できますか?

はい、ただしサードパーティのインポートを介して:ScalazはMonadEitherインスタンスを提供します。

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からのものです。

編集:

私はあなたのこの質問を逃しました。

Eitherに独自の「モナドインスタンス」を提供したいとしたら、どうすればそれができますか?

Scala for内包表記は、単に.map.flatMap.withFilter、 .filter 関連するオブジェクトに対する.foreach呼び出し。 (完全な変換スキーム here を見つけることができます。)したがって、一部のクラスに必要なメソッドがない場合は、次のコマンドを使用してクラスにaddできます暗黙の変換。

新しい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)
16
missingfaktor

Scala 2.12、Either現在右バイアス

ドキュメント から:

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)
12
Matt Klein