Max Power
Max Power

Reputation: 992

Pass list as individual function parameters

I have a function def f(a: Int, b: Int, c: Int, d: Int, ...) and I'd like to supply a list of consecutive integers as parameters (in a unit test).

Is there a neat way to supply (1 to N).toList to f? Since the function is not def f(x: Int*) I cannot simply use list: _* with list the list of integers.

Upvotes: 5

Views: 136

Answers (2)

jwvh
jwvh

Reputation: 51271

If you can't modify f() then you're kinda stuck. One (not terrific) thing you could do is write an implicit translator.

Let's pretend that f() takes 4 Int args.

implicit class Caller4[A,R](func: (A,A,A,A)=>R) {
  def call(as :A*) :R = func(as(0),as(1),as(2),as(3))
}

(f _).call(1 to 4:_*)    //call f(1,2,3,4)

The good new is that this will work for any/all methods that take 4 parameters of the same type.

The bad news is that you need a different translator for every required arity, and the compiler won't catch it if you invoke call() with too few parameters.

Upvotes: 1

Kolmar
Kolmar

Reputation: 14224

I don't think you can do it in standard Scala in a typesafe way, but it's possible to do it using the Shapeless library. Even if you don't use this library in the project, you can depend on it only for the test scope.

Here is the code sample:

import shapeless._
import shapeless.ops.function._
import shapeless.ops.traversable._

def applyToList[F, HL <: HList, R](f: F)(args: List[Any])(implicit
  // convert `(T1, T2, ...) => R` to a function from a single HList argument `HL => R`
  fnToProduct: FnToProduct.Aux[F, HL => R],  
  // convert the argument list to the HList, expected by the converted function
  toHList: FromTraversable[HL]
): R = {
  toHList(args) match {
    case Some(hargs) => fnToProduct(f)(hargs)
    case None => sys.error("argument list not applicable to function")
  }
}

Using it is as simple as this:

def f(a: Int, b: Int, c: Int, d: Int): Any = ???
applyToList(f _)(List(1, 2, 3, 4))

But notice the need for an explicit eta-conversion f _, because the Scala compiler doesn't really know, that F is a function.

You can change the definition to return Option[R], because it's impossible to know at compile-time if the provided List will fit the function or not. For example, the argument list may have a non-matching number of elements. But if this is for unit-tests, you may as well just throw an exception.

Upvotes: 2

Related Questions