Scala地图右或返回左

时间:2015-12-31 10:57:46

标签: scala either

是否可以与Either类似的方式处理Option?在Option中,我有一个getOrElse函数,Either我要返回Left或处理Right。我正在寻找最快捷的方式,而不需要任何样板:

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

5 个答案:

答案 0 :(得分:15)

在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

所以你可以做到

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

如果您发现它比fold更具可读性。

答案 1 :(得分:6)

您可以使用.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

答案 2 :(得分:5)

cchantep和Marth都是解决您眼前问题的好方法。但更广泛地说,很难将Either视为完全类似于Option的东西,特别是在让你表达可理解的计算量序列时。要么有投影API(在cchantep的解决方案中使用),但它有点破碎。 (要么用警卫,模式匹配或变量赋值进行理解,要么突破。)

FWIW,我写了library来解决这个问题。它用this API扩充了Either。您为Eithers定义了“偏见”。 “右偏”意味着普通流(map,get等)由Right对象表示,而Left对象表示某种问题。 (正确的偏见是常规的,但如果您愿意,也可以定义左偏见。)然后您可以将Either视为Option;它是一个完全类似的API。

import com.mchange.leftright.BiasedEither

import BiasedEither.RightBias._

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

更有用的是,您现在可以将Either视为真正的scala monad,即使用flatMap,map,filter和for comprehensions:

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测试失败,则不会出现异常,locPopPair只会是Left("EMPTY")

答案 3 :(得分:2)

我猜你可以这样做。

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

答案 4 :(得分:0)

在scala 2.13中,您可以使用myEither.getOrElse

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="BorderBrush" Value="Black" />
        <Setter Property="Background" Value="Red" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="VerticalContentAlignment" Value="Stretch" />
    </Style>
</ListView.ItemContainerStyle>