【问题标题】:Scala Either map Right or return LeftScala 要么映射 Right 要么返回 Left
【发布时间】:2015-12-31 10:57:45
【问题描述】:

是否可以像处理Option 一样处理Either?在Option 中,我有一个getOrElse 函数,在Either 中我想返回Left 或处理Right。我正在寻找没有任何样板的最快方法,例如:

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

【问题讨论】:

  • 左下角的值是多少?你是说leftValue吗?

标签: scala either


【解决方案1】:

在 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 更具可读性。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      cchantep 和 Marth 都是解决您眼前问题的好方法。但更广泛地说,很难将 Either 视为与Option 完全类似的东西,特别是在让您表达可能失败的推导式计算序列时。要么有一个投影 API(在 cchantep 的解决方案中使用),但它有点坏了。 (两者的投影都通过警卫、模式匹配或变量赋值进行理解。)

      FWIW,我写了一个library 来解决这个问题。它用this API 扩充了Either。你为你的任何一个定义一个“偏见”。 “右偏”意味着普通流(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 推导:

      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]

      它稍微复杂一些,但定义一个空标记是个好主意。让我们看一下上面 for 理解的细微变化:

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

      如果测试p.x &gt; 1000 失败会怎样?我们想要返回一些表示“空”的Left,但没有普遍适用的值(并非所有Left都是Left[String]。截至目前,代码会抛出一个@ 987654339@。但是我们可以自己指定一个空token,如下:

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

      【讨论】:

        【解决方案4】:

        我猜你可以这样做。

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

        【讨论】:

        • 看起来foo 应该返回String,而不是Either。最后是不是缺少.getOrElse(value)
        • “要么我想返回左要么处理右”是通过映射右投影来完成的。对于Either[A, B],目标是获得A(为Right 提供B =&gt; A)或B(为Left 提供A =&gt; B),然后.fold 是你的朋友.
        • 你说得对,我读的太快了。我只是查看了其中的代码,并认为问题是“我如何更简洁地编写这个”。我的错。
        【解决方案5】:

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

           Right(12).getOrElse(17) // 12
           Left(12).getOrElse(17)  // 17

        【讨论】:

        • 这与所要求的相反。 OP 想要返回 Left 并替换 Right
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-24
        • 1970-01-01
        • 2015-02-24
        • 2011-06-15
        • 2012-10-02
        • 2011-04-02
        • 1970-01-01
        相关资源
        最近更新 更多