How can you define a function myEval(f, args)
in Scala which takes as input another function f
and arguments args
and whose output is f(args)
?
I don't want myEval
to have any prior knowledge about the arity or argument types of f
.
Why is this useful? It is one way to solve the problem of implementing a generic timeMyFunction(f, args)
method. If there's a way to do this by some sort of lazy val construction, that would also be interesting.
Edit: The better way to implement a timing method is explained in this question. By calling timeMyFunction( { f(args) } )
, the function call is wrapped in an anonymous function Unit => Unit
. So timeMyFunction
only needs to take 0-arity functions.
Edit 2: See Dirk's answer for what is perhaps a more efficient way which avoids an anonymous function by passing f
by reference.
So my justification for the question is now purely my Scala education.
The Scala standard library isn't going to help you generalize over arity in most cases, but Shapeless is perfect for this. Here's how you could write your function in Shapeless 1.2.4:
import shapeless._
def foo[F, P <: Product, A <: HList, R](f: F, p: P)(implicit
fl: FnHListerAux[F, A => R],
pl: HListerAux[P, A]
): R = fl(f)(pl(p))
And then:
scala> foo((i: Int, s: String) => s * i, (3, "a"))
res0: String = aaa
It looks complicated, but essentially you're just saying that you need evidence that a function f
of some arbitrary arity can be converted to a single-argument function from a heterogeneous list A
to the result R
, and that the tuple P
can be converted to a heterogeneous list of the same type.
An alternative using pass-by-name would be:
def timeIt[T](thunk: =>T): T = {
// ... set-up the timer
val answer: T = thunk
// ... evaluate the result of the timer
answer // in case, you need the result and want the timing to
} // happen just as a side-effect
timeIt(someFunction(someArg1, ...))
Though this looks as if it would call someFunction
directly, it does not, since timeIt
takes the argument "by name", i.e., the scala compiler generates a hidden closure, which performs the call, when the actual value is needed in timeIt
itself.
This variant may introduce some timing noise due to the overhead of the "pass-by-name" convention.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With