Pergunta

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.

Foi útil?

Solução

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.

Outras dicas

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top