2

Say I have some code:

def foo(s:String):Either[Bar, Baz] = // some code here ... 

I want to use it as:

val a = Array("a", "b", "c")
a.map(foo) match {
   case something => // should match when all elements in array are of type Right
   case _ =>
}

Can anyone suggest code for "something"

EDIT: Prefer to match and use array of Right[Bar,Baz] directly rather than having to extract after match.

1
  • Either will do. Either.RightProjection[Baz] will be preferred. Commented Sep 22, 2012 at 5:57

3 Answers 3

2

Use the forall method to check that all elements in the array are isRight:

a.map(foo) match {
  case eithers if eithers.forall(_.isRight) =>
  case _ =>
}

Regarding your comment, if you want the match and convert-to-right all in one pass, then try a custom extractor:

object RightArrayExtractor {
  def unapply(eithers: Array[Either[Bar, Baz]]) =
    eithers.foldLeft(Option(Vector[Right[Bar, Baz]]())) {
      case (Some(z), x @ Right(_)) => Some(z :+ x)
      case (None, _) => None
      case (_, Left(x)) => None
    }
}

a.map(foo) match {
  case RightArrayExtractor(eithers) => // eithers is a Vector[Right[Bar,Baz]]
  case _ =>
}
Sign up to request clarification or add additional context in comments.

2 Comments

that solves part of the problem. I want to filter and use at the same time.. so now I have case eithers if eithers.forall(_.isRight) => eithers.map(_.right). I would have liked case a:Array[Right[Baz]] => // do something with a.
@dhs. Thanks. Saw the update. Waiting a bit to see if any other answers.
1

You can use the convenience of collect to change the array to the Right type (no pun intended) and the fact that the collected array will have the same size if all elements are Right:

a.map(foo).collect{case r@Right(_) => r} match {
  case a1 if a1.size == a.size =>
    // do something with a1 of type Array[Right[Bar,Baz]]
  case _ => 
}

Comments

1

The other answers are very good and this example isn't that practical as the others. I'd just like to add a bit of underlying theory.

What you're describing is often called traversal in functional programming. You have a collection like Seq[X] and a monadic (or applicative) computation X => M[Y]. Standard map gives you Seq[M[Y]], but traversal gives you M[Seq[Y]].

In this case, the monadic computation is something that produces Either[Error,Right], in this case M[_] is Either[Error,_]. So if you just map over a collection with such a function, you get Seq[Either[Error,Right]]. But what you want is Either[Error,Seq[Right]] and that's exactly what traversal does. If the function fails on any element of the sequence (returns Left(something)) then the final result is just this Left(something). If the function succeeds on all the elements (returns Right(...) for all of them) then the final result is Right(sequenceOfResults).

Scala doesn't have a built-in function for that, but Scalaz does, it's called traverse. A complete example:

import scalaz._;
import Scalaz._;
import Applicative._;

object RightMatch extends App {
  // our example function
  def foo(s: String): Either[String,Int] =
    if (s.startsWith("a")) Right(s.length)
    else Left("wrong: " + s);

  // We make an utility function for traversing Sequences wit Eithers:
  def traverseRight[X,L,R](es: Seq[X], f: X => Either[L,R]): Either[L,Seq[R]] = {
    // we need to convert Either to Either.RightProjection
    type RightF[Y] = Either.RightProjection[L,Y];
    es.traverse[RightF,R](x => f(x).right).e; // and back to Either
  }

  // Or, if we just want to convert an existing sequence of eithers:
  def traverseRight[L,R](es: Seq[Either[L,R]]): Either[L,Seq[R]] =
    traverseRight(es, identity[Either[L,R]]);

  {
    val a = Seq("a", "ab", "ac");
    traverseRight(a, foo) match {
      case Right(arr) => println(arr); // we get the array of Ints here
      case Left(err)  => println(err); // we get String here (the first error)
    }
  }
}

(Note that in Scalaz Array has not an implementation of traversable (I don't know why), so I used Seq instead.)

As mentioned, traversal applies not just to Eithers, it applies to any monadic computations. So the same approach can be used for a vast range of problems, like sequencing stateful computations (modelled by scalaz's State), sequencing non-deterministic computations (the List monad) etc.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.