43

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 Options. I'd like to use it with Eithers, but the necessary methods are not present in the default imports.

for {
  foo <- Right(1)
  bar <- Left("nope")
} yield (foo + bar)

// expected result: Left("nope")
// instead I get "error: value flatMap is not a member..."

Is this functionality available through some import?

There is a slight hitch:

for {
  foo <- Right(1)
  if foo > 3
} yield foo
// expected result: Left(???)

For a List, it would be List(). For Option, it would be None. Do the Scala standard libraries provide a solution to this? (Or perhaps scalaz?) How? Suppose I wanted to provide my own "monad instance" for Either, how could I do that?

Dan Burton
  • 53,238
  • 27
  • 117
  • 198
  • great question, @DanBurton, assume related to your epic answer, http://stackoverflow.com/a/10866383/185840 from yesterday – virtualeyes Jun 03 '12 at 08:24
  • 1
    @virtualeyes quite right. Almost all of my questions are offshoots from seeing or providing an answer to someone else's question. – Dan Burton Jun 04 '12 at 02:38

3 Answers3

56

It doesn't work in scala 2.11 and earlier because Either is not a monad. Though there's talk of right-biasing it, you can't use it in a for-comprehension: you have to get a LeftProject or RightProjection, like below:

for {
  foo <- Right[String,Int](1).right
  bar <- Left[String,Int]("nope").right
} yield (foo + bar)

That returns Left("nope"), by the way.

On Scalaz, you'd replace Either with Validation. Fun fact: Either's original author is Tony Morris, one of Scalaz authors. He wanted to make Either right-biased, but was convinced otherwise by a colleague.

Zoltán
  • 21,321
  • 14
  • 93
  • 134
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 2
    Yes ! Right-biased like it is in Haskell actually. You can flatMap Either but only on Right. Because the Functor instance for `Either` is based on `Either a` ^^ – Andy Petrella Jun 03 '12 at 19:15
  • any more info on why he was `convinced otherwise by a colleague.`? – Kevin Meredith Apr 09 '15 at 16:05
  • @KevinMeredith I don't recall, and I don't think I ever really knew it. I think I heard it from Tony himself, in a "oh, why did I let that happen" sort of tone, but it's been a long time. – Daniel C. Sobral Apr 09 '15 at 18:02
17

As of Scala 2.12, Either is now right biased

From the documentation:

As Either defines the methods map and flatMap, it can also be used in for comprehensions:

val right1: Right[Double, Int] = Right(1)
val right2                     = Right(2)
val right3                     = Right(3)
val left23: Left[Double, Int]  = Left(23.0)
val left42                     = Left(42.0)

for (
  a <- right1;
  b <- right2;
  c <- right3
) yield a + b + c // Right(6)

for (
  a <- right1;
  b <- right2;
  c <- left23
) yield a + b + c // Left(23.0)

for (
  a <- right1;
  b <- left23;
  c <- right2
) yield a + b + c // Left(23.0)

// It is advisable to provide the type of the “missing” value (especially the right value for `Left`)
// as otherwise that type might be infered as `Nothing` without context:
for (
  a <- left23;
  b <- right1;
  c <- left42  // type at this position: Either[Double, Nothing]
) yield a + b + c
//            ^
// error: ambiguous reference to overloaded definition,
// both method + in class Int of type (x: Char)Int
// and  method + in class Int of type (x: Byte)Int
// match argument types (Nothing)
Matt Klein
  • 7,856
  • 6
  • 45
  • 46
16

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
<console>: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)
Community
  • 1
  • 1
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • so, no way to use if guards? Tough if that is the case, would be quite powerful to combine Either enabled for comprehension with guards. I can get it to compile, post-guard condition, with a "e <- either.right.toOption" – virtualeyes Jun 03 '12 at 09:00
  • @virtualeyes: "so, no way to use if guards?" Sure, there is. Just pimp `.filter` and `.withFilter` on `Either` with whatever behavior you want it to support. – missingfaktor Jun 03 '12 at 09:00
  • k, will have to dive in to the link you provided, this is advanced pimping ;-) Probably thinking something is possible that is not with Either enabled for comprehensions; check Dan Burton's excellent Haskell reply to my question on if/else conditional branching with embedded return (http://stackoverflow.com/a/10866383/185840). Basically trying to do the same with Either for {...} where you get a Left/Right outcome vs. Some/None – virtualeyes Jun 03 '12 at 09:05
  • @virtualeyes, yes, I am quite familiar with the technique. What about it? Doesn't seem related to `filter`ing or `if`-guards in any way. – missingfaktor Jun 03 '12 at 09:08
  • @virtualeyes, for what it's worth, the stop-at-first-failure behavior is provided by `Monad` instances of both `Either` and `scalaz.Validation` (a data structure isomorphic to `Either`). – missingfaktor Jun 03 '12 at 09:21
  • 2
    @missingfaktor I don't see any mention of the `.filter` method in version 2.9 of the spec: `The precise meaning of generators and guards is defined by translation to invocations of four methods: map, withFilter, flatMap, and foreach.` – rxg Jun 03 '12 at 16:00
  • @rxg, thank you for correcting. I'll update my answer accordingly. – missingfaktor Jun 03 '12 at 16:00