0

I am looking for a function that will map over a collection coll: Seq[A] while applying a function f: A => B and returning a Seq[Either[Throwable, B]] so that errors can be handled downstream.

Is there a function similar to this that is pre-baked into some library? Perhaps Cats or Scalaz?

See my implementation below:

import cats.syntax.either._

def eitherMap[A,B](f: A => B, coll: Seq[A]): Seq[Either[Throwable, B]] = {
  coll.map { elem => 
      Either.catchNonFatal(f(elem))
  }
}
D Cohen
  • 157
  • 1
  • 7
  • 2
    Do you really need `Either`? It looks like `Try` would do it: `coll.map(a => Try(f(a)))` – jwvh Nov 07 '18 at 18:02
  • If you _really_ need `Either`: `coll.map(a => Try(f(a)).toEither)` – jwvh Nov 07 '18 at 18:10
  • Such function would seem too specific for me. Btw, it feels a bit weird to see a signature that takes `A => B` and then somehow magically that turns into an Either in the response. I'd rather take `A => Either[Throwable, B]]` as a parameter and I'd pass `f andThen Either.catchNonFatal` to it. – slouc Nov 07 '18 at 18:11

2 Answers2

2

Per jwvh: coll.map(a => Try(f(a)).toEither) seems to be the simplest/cleanest way to accomplish this.

D Cohen
  • 157
  • 1
  • 7
0

Unfortunately I don't believe what you're looking for already exists exactly... But here is a generic implementation that does what you want and perhaps further reading of the ApplicativeError type class in cats will give you something closer

type ErrorOr[A] = Either[Throwable, A]

object MapAttempt {
  implicit class MapAttemptOps[A, F[_] : Functor](fa: F[A]) {
    def mapAttempt[B, G[_]](f: A => B)(implicit appErr: ApplicativeError[G, Throwable]): F[G[B]] =
      fa.map(a => appErr.catchNonFatal(f(a)))
  }
}

import MapAttempt._

List(0, 1, 2, 3, 4).mapAttempt[Int, ErrorOr](5 / _)    

Returns:

res0: List[ErrorOr[Int]] = List(Left(java.lang.ArithmeticException: / by zero), Right(5), Right(2), Right(1), Right(1))

Edit: The separation of trait and syntax isn't necessary so I removed it

ChocPanda
  • 88
  • 6