web-dev-qa-db-ja.com

Scala右にマップするか、左に戻る

EitherOptionと同様の方法で処理することは可能ですか? OptionにはgetOrElse関数があり、EitherにはLeftまたはプロセスRightを返したいです。私はこれのような定型文なしでこれを行う最速の方法を探しています:

val myEither:Either[String, Object] = Right(new Object())
myEither match {
    case Left(leftValue) => value
    case Right(righValue) => 
        "Success"
}
12
Michał Jurczuk
16
Alexey Romanov

.fold を使用できます。

scala> val r: Either[Int, String] = Right("hello")
r: Either[Int,String] = Right(hello)

scala> r.fold(_ => "got a left", _ => "Success")
res7: String = Success

scala> val l: Either[Int, String] = Left(1)
l: Either[Int,String] = Left(1)

scala> l.fold(_ => "got a left", _ => "Success")
res8: String = got a left

編集:

あなたの質問を読み直すと、Leftの値を返すのか、別の値(別の場所で定義)を返すのかがわかりません。
前者の場合、identity.foldに渡すことができますが、これにより戻り値の型がAnyに変更される可能性があります。

scala> r.fold(identity, _ => "Success")
res9: Any = Success
6
Marth

CchantepとMarthはどちらも、当面の問題に対する優れたソリューションです。しかし、より広く言えば、EitherをOptionに完全に類似したものとして扱うことは、特に、理解のために失敗する可能性のある計算のシーケンスを表現できるようにする場合には困難です。どちらもプロジェクションAPI(cchantepのソリューションで使用)を持っていますが、少し壊れています。 (どちらの予測も、ガード、パターンマッチング、または変数の割り当てによる理解のために割り込んでいます。)

FWIW、私はこの問題を解決するために library を作成しました。 this API を使用して拡張します。 Eithersの「バイアス」を定義します。 「正しいバイアス」とは、通常のフロー(map、getなど)がRightオブジェクトによって表される一方で、Leftオブジェクトが何らかの問題を表すことを意味します。 (右バイアスは従来通りですが、必要に応じて左バイアスを定義することもできます。)次に、EitherOptionのように扱うことができます。完全に類似したAPIを提供します。

_import com.mchange.leftright.BiasedEither

import BiasedEither.RightBias._

val myEither:Either[String, Object] = ...
val o = myEither.getOrElse( "Substitute" )
_

より便利に、Eitherをtrue scalaモナドのように扱うことができます。つまり、flatMap、map、filterを使用して、内包表記に使用できます。

_val myEither : Either[String, Point] = ???
val nextEither = myEither.map( _.x ) // Either[String,Int]
_

または

_val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???

val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p )
  } yield {
    (p, g.population)
  }
}
_

すべての処理ステップが成功した場合、locPopPairは_Right[Long]_になります。問題が発生した場合は、最初に_Left[String]_が発生します。

少し複雑ですが、空のトークンを定義することをお勧めします。上記の理解度のわずかな変化を見てみましょう:

_val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p ) if p.x > 1000
  } yield {
    (p, g.population)
  }
}
_

テスト_p.x > 1000_が失敗した場合はどうなりますか? 「空」を表すLeftを返したいのですが、普遍的な適切な値はありません(すべてのLeftが_Left[String]_であるとは限りません。現在のところ、何が起こるかはコードはNoSuchElementExceptionをスローしますが、次のように自分で空のトークンを指定できます。

_import com.mchange.leftright.BiasedEither

val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY")
import RightBias._

val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???

val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p ) if p.x > 1000
  } yield {
    (p, g.population)
  }
}
_

これで、_p.x > 1000_テストが失敗した場合、例外は発生せず、locPopPairLeft("EMPTY")になります。

5
Steve Waldman

次のようにできると思います。

def foo(myEither: Either[String, Object]) = 
  myEither.right.map(rightValue => "Success")
2
cchantep