Different ways of passing a function to another function

Hi,
Given the following definitions for methods below,

def asyncExec(f: => Unit) = {
println(“calling f from asyncExec”)
f
}

def test() = println(“This is a test”)

I expect the following calls to asyncExec with test passed differently would result in different outputs:

The call, asyncExec(test), should generate the following output,

calling f from asyncExec
This is a test

While the call, asyncExec(test()), should generate the following output,

This is a test
calling f from asyncExec

But, the fact is that both calls generate the same output as the following

calling f from asyncExec
This is a test

This confuses me as the call, asyncExec(test()), should have executed test() first.

Looking for explanations for this behavior and thanks for looking into my post.

Hi lyfjura, yes indeed working with function is Scala is really confusing, especially for beginners. Many things you assume about functional languages just aren’t true, and there are many gotchas dealing with functions as arguments. One would think that dealing with functions in a functional language would be less tricky.

As I understand your definition of asynExec is not what you think it is. you’ve defined a function with a pass-by-name argument, not a function with a function as argument. The parameter definition f:=> Unit means that at every call-site the argument is wrapped (by the compiler) with an additional lambda(...), and references to f within asyncExec are replaced with something like funcall(f). The illusion is that you can replace f in each call by the text from the callsite (assuming there are no name conflicts within asyncExec). For example if the call-site uses a variable named f, then the evaluation will still reference the call-site f as you intuitively expect.

Another gotcha to watch out for is that Scala tries to make the parens optional in a function call with no arguments. This causes grief in many cases: sometimes what you think is a function is actually a method which cannot be passed by value, and sometimes the compiler tries to insert a function call where you’d like just the function value (frustrating but you get used to it eventually). If you really want to insist that you mean the function f rather than the return value of calling f, you can often (but not always) use the obscure syntax f _ with no parentheses; note that f(_) has yet another meaning.

1 Like

Let’s look at the signature of asyncExec:

def asyncExec(f: => Unit)

This is a function, that expects an argument of type Unit. The => changes the time it is evaluated, but not the type. The whole expression given as argument to asyncExec will be evaluated, when f is used inside the method, which includes the call via parentheses.

So in your call

asyncExec(test())
       // ^^^^^^ this all is the expression, that is evaluated lazily

If you wanted to execute the call to test before the method, you’d have to call it outside the parameter list of asyncExec

2 Likes

My confusion here was about the difference between,
def asyncExec(f: () => Unit) vs def asyncExec(f: => Unit)
Thank you for the explanation.

This is very helpful for me in understanding the difference between

def asyncExec(f: () => Unit) and def asyncExec(f: => Unit).

Thanks you.