Using Eithers with Scala “for” syntax

前端 未结 3 835
盖世英雄少女心
盖世英雄少女心 2020-12-18 17:38

As I understand it, Scala \"for\" syntax is extremely similar to Haskell\'s monadic \"do\" syntax. In Scala, \"for\" syntax is often used for Lists and Op

3条回答
  •  臣服心动
    2020-12-18 18:10

    Is this functionality available through some import?

    Yes, but via a third party import: Scalaz provides a Monad instance for Either.

    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)
    

    Now if-guard is not a monadic operation. Therefore if you attempt to use if-guard, it results in a compiler error as expected.

    scala> for {
         |   foo <- 1.right[String]
         |   if foo > 3
         | } yield foo
    :18: error: value withFilter is not a member of Either[String,Int]
                    foo <- 1.right[String]
                                  ^
    

    The convenience methods used above - .right and .left - are also from Scalaz.

    Edit:

    I missed this question of yours.

    Suppose I wanted to provide my own "monad instance" for Either, how could I do that?

    Scala for comprehensions are simply translated to .map, .flatMap, .withFilter, and .filter .foreach calls on the objects involved. (You can find the the full translation scheme here.) So if some class does not have the required methods, you can add them to a class using implicit conversions.

    A fresh REPL session below.

    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)
    

提交回复
热议问题