Scala Either map Right or return Left

别等时光非礼了梦想. 提交于 2019-12-06 19:01:06

问题


Is it possible to handle Either in similar way to Option? In Option, I have a getOrElse function, in Either I want to return Left or process Right. I'm looking for the fastest way of doing this without any boilerplate like:

val myEither:Either[String, Object] = Right(new Object())
myEither match {
    case Left(leftValue) => value
    case Right(righValue) => 
        "Success"
}

回答1:


In Scala 2.12,

Either is right-biased, which means that Right is assumed to be the default case to operate on. If it is Left, operations like map, flatMap, ... return the Left value unchanged

so you can do

myEither.map(_ => "Success").merge

if you find it more readable than fold.




回答2:


You can use .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

Edit:

Re-reading your question it's unclear to me whether you want to return the value in the Left or another one (defined elsewhere)
If it is the former, you can pass identity to .fold, however this might change the return type to Any:

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



回答3:


Both cchantep's and Marth's are good solutions to your immediate problem. But more broadly, it's difficult to treat Either as something fully analogous to Option, particularly in letting you express sequences of potentially failable computations for comprehensions. Either has a projection API (used in cchantep's solution), but it is a bit broken. (Either's projections break in for comprehensions with guards, pattern matching, or variable assignment.)

FWIW, I've written a library to solve this problem. It augments Either with this API. You define a "bias" for your Eithers. "Right bias" means that ordinary flow (map, get, etc) is represented by a Right object while Left objects represent some kind of problem. (Right bias is conventional, although you can also define a left bias if you prefer.) Then you can treat the Either like an Option; it offsers a fully analogous API.

import com.mchange.leftright.BiasedEither

import BiasedEither.RightBias._

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

More usefully, you can now treat Either like a true scala monad, i.e. use flatMap, map, filter, and for comprehensions:

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

or

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)
  }
}

If all processing steps succeeded, locPopPair will be a Right[Long]. If anything went wrong, it will be the first Left[String] encountered.

It's slightly more complex, but a good idea to define an empty token. Let's look at a slight variation on the for comprehension above:

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

What would happen if the test p.x > 1000 failed? We'd want to return some Left that signifies "empty", but there is no universal appropriate value (not all Left's are Left[String]. As of now, what would happen is the code would throw a NoSuchElementException. But we can specify an empty token ourselves, as below:

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)
  }
}

Now, if the p.x > 1000 test fails, there will be no Exception, locPopPair will just be Left("EMPTY").




回答4:


I guess you can do as follows.

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


来源:https://stackoverflow.com/questions/34545394/scala-either-map-right-or-return-left

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!