Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert functions raising exceptions to functions returning Either?

Suppose I have a few functions that raise exceptions. I am wrapping them to return Either[Throwable, <function return type>]. (Let's assume I need Either rather than Try).

def fooWrapper(arg1: FooArg1, arg2: FooArg2) =
  try Right(foo(arg1, arg2)) catch { case NonFatal(e) => Left(e) }

def barWrapper(arg1: BarArg1, arg2: BarArg2, a3: BarArg3) =
  try Right(bar(arg1, arg2, artg3)) catch { case NonFatal(e) => Left(e) }

...

Now I would like to write a generic wrapper to get rid of the bolierpllate code. What would you suggest ?

like image 488
Michael Avatar asked May 13 '14 14:05

Michael


1 Answers

Any time you want to make something generic with respect to arity, Shapeless is pretty likely to have what you need. In this case you can write the following:

import scala.util.control.NonFatal
import shapeless._, ops.function._

def safeify[F, A <: HList, R, O](f: F)(implicit
  ftp: FnToProduct.Aux[F, A => R],
  ffp: FnFromProduct[A => Either[Throwable, R]]
) = ffp((a: A) =>
  try Right(ftp(f)(a)) catch {
    case NonFatal(ex) => Left(ex)
  }
)

Now suppose we have an unsafe method like the following:

def bad(s: String, i: Int) = s.toInt / i

We can wrap it:

scala> val better = safeify(bad _)
better: (String, Int) => Either[Throwable,Int] = <function2>

And now we don't have to worry about exceptions:

scala> better("1", 0)
res0: Either[Throwable,Int] = Left(ArithmeticException: / by zero)

scala> better("a", 1)
res1: Either[Throwable,Int] = Left(NumberFormatException: For input string: "a")

This will work for any old FunctionN.

like image 82
Travis Brown Avatar answered Nov 16 '22 00:11

Travis Brown