1
\$\begingroup\$

I wrote a function of type Seq[Either[String,Int]] => Either[String, Seq[Int]]

scala> xs
res11: List[Either[String,Int]] = List(Right(1), Right(55))

scala> acc
res12: Either[String,Seq[Int]] = Right(List())

scala> xs.foldLeft(acc){ (acc, elem) => {
     |     acc.right.flatMap(list => elem.right.map(_ +: list) )
     | }}
res10: Either[String,Seq[Int]] = Right(List(55, 1))

Please review my foldLeft solution. Perhaps there's a higher order function that can more elegantly do this work?

\$\endgroup\$
1
  • \$\begingroup\$ You haven't written a function. There's no def in there. It seems you've demonstrated the execution sequence which could be rewritten as a function. Why not write up the actual function properly? \$\endgroup\$
    – itsbruce
    Commented Aug 13, 2015 at 10:33

2 Answers 2

9
\$\begingroup\$

A few points:

  1. Your implementation reverses the order of elements if it succeeds (i.e. if they're all rights). I'm assuming this isn't intentional?
  2. You're taking a list but returning a Seq. This may be what you want, but I find it a little confusing.
  3. The inner curly brackets are unnecessary (and I personally find them kind of noisy).
  4. I'd strongly suggest not shadowing the external acc with the accumulator function argument.

So I'd probably write it like this:

val xs: List[Either[String, Int]] = List(Right(1), Right(55))

xs.foldRight(Right(Nil): Either[String, List[Int]]) { (elem, acc) =>
  acc.right.flatMap(list => elem.right.map(_ :: list))
}

Or:

xs.foldRight(Right(Nil): Either[String, List[Int]]) { (elem, acc) =>
  for {
    t <- acc.right
    h <- elem.right
  } yield h :: t
}

But that's only if I couldn't use cats or Scalaz, both of which provide this operation as sequenceU:

scala> import cats._, cats.syntax.traverse._, cats.std.all._
import cats._
import cats.syntax.traverse._
import cats.std.all._

scala> val xs: List[Either[String, Int]] = List(Right(1), Right(55))
xs: List[Either[String,Int]] = List(Right(1), Right(55))

scala> xs.sequenceU
res0: scala.util.Either[String,List[Int]] = Right(List(1, 55))

Which is much nicer.

\$\endgroup\$
2
  • \$\begingroup\$ +1 for foldRight. When you say "You're taking a list but returning a Seq", well no. Having a function which has a signature taking an input of type Seq and returning one is good. More generic and when given a list, if written properly, it'll return one. What is confusing is using a list in that function's implementation. But then the OP hasn't formally defined a function, just typed into the repl a sequence showing how it could be written. \$\endgroup\$
    – itsbruce
    Commented Aug 13, 2015 at 11:01
  • \$\begingroup\$ @itsbruce It's hard to say without knowing more about the OP's intent (as you say, there's nothing like a method signature to make that clear). There are plenty of cases where it you wouldn't want to lose the static information that what you're working with is a List. \$\endgroup\$ Commented Aug 13, 2015 at 13:20
-1
\$\begingroup\$

Probably not the most glamorous solution (feel free to efficientiate) to the problem, but you could always...

    def transmogrify[A, B](someSeq: Seq[Either[A, B]]): Either[A, Seq[B]] = {
      if (someSeq.exists(_.isLeft)) Left(someSeq.collect( { case Left(anA: A) => anA }).head)
      else Right(someSeq.collect { case Right(aB: B) => aB })
    }
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and how it improves upon the original) so that the author can learn from your thought process. \$\endgroup\$
    – Malachi
    Commented Aug 13, 2015 at 16:50

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.