10

I'm probably missing something that's right in the documentation, but I can't really make much sense of it - I've been teaching myself Scala mostly by trial and error.

Given a function f: A => C, what is the idiomatic way to perform the following conversions?

Either[A, B] -> Either[C, B]

Either[B, A] -> Either[B, C]

(If I have two such functions and want to convert both sides, can I do it all at once or should I apply the idiom twice sequentially?)

Option[A] -> Option[C]

(I have a feeling that this is supposed to use for (...) yield somehow; I'm probably just blanking on it, and will feel silly when I see an answer)

And what exactly is a "projection" of an Either, anyway?

axel22
  • 32,045
  • 9
  • 125
  • 137
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • 1
    From a comment below you seem to be confusing `Either` with a `Pair`, a `Tuple2`. That is not the case: an `Either` can only contain _one_ value. In a sense, it is similar to `union` in C, except that you know which member is actually stored. – Daniel C. Sobral Sep 21 '11 at 21:37
  • I'm aware of that, but I still get confused one projections are thrown into the mix. I didn't really express that one clearly. – Karl Knechtel Sep 21 '11 at 21:42

3 Answers3

18

You do either a:

either.left.map(f)

or a:

either.right.map(f)

You can also use a for-comprehension: for (x <- either.left) yield f(x)

Here's a more concrete example of doing a map on an Either[Boolean, Int]:

scala> val either: Either[Boolean, Int] = Right(5)
either: Either[Boolean, Int] = Right(5)

scala> val e2 = either.right.map(_ > 0)
either: Either[Boolean, Boolean] = Right(true)

scala> e2.left.map(!_)
either: Either[Boolean, Boolean] = Right(true)

EDIT:

How does it work? Say you have an Either[A, B]. Calling left or right creates a LeftProjection or a RightProjection object that is a wrapper that holds the Either[A, B] object.

For the left wrapper, a subsequent map with a function f: A => C is applied to transform the Either[A, B] to Either[C, B]. It does so by using pattern matching under the hood to check if Either is actually a Left. If it is, it creates a new Left[C, B]. If not, it just changes creates a new Right[C, B] with the same underlying value.

And vice versa for the right wrapper. Effectively, saying either.right.map(f) means - if the either (Either[A, B]) object holds a Right value, map it. Otherwise, leave it as is, but change the type B of the either object as if you've mapped it.

So technically, these projections are mere wrappers. Semantically, they are a way of saying that you are doing something that assumes that the value stored in the Either object is either Left or Right. If this assumption is wrong, the mapping does nothing, but the type parameters are changed accordingly.

axel22
  • 32,045
  • 9
  • 125
  • 137
  • Break down how this works for me, please? Something to do with the projections? Evidently `.left` and `.right` here are not like in other languages with library "pair" types, where they would just access the members of type [A] and [B] respectively. – Karl Knechtel Sep 21 '11 at 21:22
  • I've edited the answer to explain a little bit of what happens under the hood. But, either is not a pair, or a tuple type, also called an intersection type - `A x B`. It holds either a member of type [A] or [B] - `A V B`. This means that you cannot safely access either member, like you could in a tuple, because only one member is present. – axel22 Sep 21 '11 at 21:36
  • ... Beautiful. And how about with an Option? Just `for (x <- option) yield f(x)`, since the Option is already iterable? – Karl Knechtel Sep 21 '11 at 21:45
  • 1
    @Karl `Option` is not an `Iterable`, but it implements the method `map`, so that is enough. – Daniel C. Sobral Sep 21 '11 at 21:49
  • The problem here is that you would need to provide 2 mapping functions to do the actual mapping of an `Either`, since there are 2 possible *types* of values inside, and you don't know which it is. You could do: `e.left.map(!_).right.map(_ * 2)`. Could you go for loop style from that. Maybe. Let me think about it.. – axel22 Sep 21 '11 at 21:49
  • With `Option` you have only possible value that could be mapped - and only one *type* for the mapping function. Here you have 2 of them - hence, you need 2 mapping functions. – axel22 Sep 21 '11 at 21:50
3

Given f: A=>B and xOpt: Option[A], xOpt map f produces the Option[B] you need.

Given f: A=>B and xOrY: Either[A, C], xOrY.left.map(f) produces the Either you are looking for, mapping just the first component; similarly you can deal with RightProjection of Either.

If you have two functions, you can define mapping for both components, xOrY.fold(f, g).

Vlad Patryshev
  • 1,379
  • 1
  • 10
  • 16
0
val e1:Either[String, Long] = Right(1)
val e2:Either[Int,Boolean] = e1.left.map(_.size).right.map( _ >1 )
// e2: Either[Int,Boolean] = Right(false)
ib84
  • 675
  • 5
  • 16