1

I have a series of functions that accept a function as an argument (func), and the only difference in them is that func accepts different numbers of parameters.

  def tryGet[T,A](func: (A) => T)
  def tryGet[T,A,B](func: (A,B) => T)
  ...       [T,A,B,C...]

Is there a way to make a function accept a function with any length argument list?

I tried using variable length argument lists, eg func:(A*) but had trouble passing any functions in as it now requires a Seq[A].

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
Jethro
  • 3,029
  • 3
  • 27
  • 56
  • If you do not know the number and type of the arguments of the function. How do you expect to call / use it? – Luis Miguel Mejía Suárez Sep 18 '19 at 14:06
  • @LuisMiguelMejíaSuárez I had expected to be able to call like so: `func()`. For example if I wanted to pass in the function `def getSomething(int:Int) = Future.successful(int)`, by calling tryGet like so `tryGet( (int:Int) => getSomething(int) )` I was using `func() map { ... handle exceptions and return an Option instead}`. The wider question i'm trying to solve is can I wrap a load of API calls that throw exceptions, so that they throw Options instead in the case of NoSuchElementExceptions. – Jethro Sep 18 '19 at 14:18
  • Eg `tryGet` shouldn't know or care what the function is, it only knows that if `func` throws some specific exceptions it should return `None`, and if it doesn't then it should return `Some(T)` – Jethro Sep 18 '19 at 14:24
  • 1
    How do you expect `func()` to compile? If it may take any number of arguments? You do not really want to pass any kind of function. But rather any kind o block that results on `Future[T]` and call something over it. Something like `def tryGet[T](block: => Future[T]): Future[Option[T]] = block.recoverWith(...)`. – Luis Miguel Mejía Suárez Sep 18 '19 at 14:24
  • 1
    Other alternative would be to just receive a function `A => T` where **A** is any kind of **product**. Thus, if you need two arguments, you pass them as a tuple. – Luis Miguel Mejía Suárez Sep 18 '19 at 14:45

2 Answers2

4

You can try to use shapeless.ops.function.FnToProduct

def tryGet[F: FnToProduct](func: F) = ???

val f1: Int => String = ???
val f2: (Int, Int) => String = ???
val f3: (Int, Int, Int) => String = ???

tryGet(f1)
tryGet(f2)
tryGet(f3)

https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#facilities-for-abstracting-over-arity

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
1

Why don't you just use scala.util.Try? Or simply pass the argument to tryGet not as a block but as a by-name parameter of type T?

  • Thanks but I don't know how to do that, an example would be great! – Jethro Sep 19 '19 at 16:24
  • 1
    `Try(unaryFunc(a)) Try(binaryFunc(a, b)).toOption` or, if you wish to do it all manually, `def tryGet[T](func: => T) = try { Some(func) } catch { case ex: Exception => None }` – Nebehr Gudahtt Sep 20 '19 at 05:57