Since you clearly don't have enough choices yet, one other way is to use ScalaUtils attempt method. ScalaUtils is a very tiny, focused library that you may not mind adding as a dependency. The attempt method (which is in the org.scalautils package object) will give you an Or type on which you can invoke toEither. I was first going to suggest doing this with Try so you wouldn't need to add any dependencies, but it doesn't appear to have the toEither method, which surprised me. So here's what it would look like with Or:
scala> import org.scalautils._
import org.scalautils._
scala> def foo(i: Int, s: String): String = { require(i >= 0); s * i }
foo: (i: Int, s: String)String
scala> def bar(b: Boolean, i: Int, s: String): Int = { require(i >= 0); if (b) s.length else i }
bar: (b: Boolean, i: Int, s: String)Int
scala> def foo2(i: Int, s: String) = attempt(foo(i, s)).toEither
foo2: (i: Int, s: String)Either[Throwable,String]
scala> def bar2(b: Boolean, i: Int, s: String) = attempt(bar(b, i, s)).toEither
bar2: (b: Boolean, i: Int, s: String)Either[Throwable,Int]
scala> foo2(2, "ho")
res10: Either[Throwable,String] = Right(hoho)
scala> foo2(-2, "ho")
res11: Either[Throwable,String] = Left(java.lang.IllegalArgumentException: requirement failed)
scala> bar2(true, 3, "ho")
res12: Either[Throwable,Int] = Right(2)
scala> bar2(false, 3, "ho")
res13: Either[Throwable,Int] = Right(3)
scala> bar2(false, -3, "ho")
res14: Either[Throwable,Int] = Left(java.lang.IllegalArgumentException: requirement failed)
Sorry, I initially missed that you wanted one method. I would probably just overload like noziar suggested:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def safely[A, B, C](f: (A, B) => C): (A, B) => Either[Throwable, C] = (a: A, b: B) => attempt(f(a, b)).toEither
def safely[A, B, C, D](f: (A, B, C) => D): (A, B, C) => Either[Throwable, D] = (a: A, b: B, c: C) => attempt(f(a, b, c)).toEither
// Exiting paste mode, now interpreting.
safely: [A, B, C](f: (A, B) => C)(A, B) => Either[Throwable,C] <and> [A, B, C, D](f: (A, B, C) => D)(A, B, C) => Either[Throwable,D]
safely: [A, B, C](f: (A, B) => C)(A, B) => Either[Throwable,C] <and> [A, B, C, D](f: (A, B, C) => D)(A, B, C) => Either[Throwable,D]
scala> val foo3 = safely { foo _ }
foo3: (Int, String) => Either[Throwable,String] = <function2>
scala> val bar3 = safely { bar _ }
bar3: (Boolean, Int, String) => Either[Throwable,Int] = <function3>
scala> foo3(2, "ho")
res5: Either[Throwable,String] = Right(hoho)
scala> foo3(-2, "ho")
res6: Either[Throwable,String] = Left(java.lang.IllegalArgumentException: requirement failed)
scala> bar3(true, 3, "ho")
res7: Either[Throwable,Int] = Right(2)
scala> bar3(false, 3, "ho")
res8: Either[Throwable,Int] = Right(3)
scala> bar3(false, -3, "ho")
res9: Either[Throwable,Int] = Left(java.lang.IllegalArgumentException: requirement failed)
If you want to avoid overloading (and shapeless), then the other alternative is the magnet pattern. I believe that would get you down to one safely method, but I think overloading would be simpler.