I'm getting some unexpected behavior when using a call by name function in Scala. Can anybody explain the difference here?
class Button( act: => Unit) {def fire = act}
def foo() {println("foo got called")}
val x= new Button(foo)
x.fire
val y= new Button(foo _)
y.fire
x.fire causes foo to be called. y.fire does not. Why? What function is being passed to y? Thanks!
You should probably define your class a bit differently.
class Button( act: () => Unit) {def fire = act()}
Notice you are now taking in a Function0[Unit]
instead of a call-by-name Unit
value. This new definition has better typesaftey since it requires a function, while yours will accept any value, and will do nothing unless there are side effect required to evaluate the value passed in. We also now call the function passed in for its side effects instead of just returning it.
The problem with Unit
is that when a unit is required any value can be provided which will be automatically discarded for a Unit
. For example, with your code new Button(5)
is valid, as is the code val x: Unit = 5
.
The reason the first code class Button( act: => Unit) {def fire = act}
"works", is you call the method foo, and pass the result into the Button
constructor. Since it's a by-name parameter foo
doesn't actually run until it's used. When you then use the Unit value passed into the class, the expression needs to be evaluated so foo
is fun.
You second example is different however, adding the underscore (foo _
) now means that you are passing in the method itself as a function, instead of calling the method and passing in the result as in your first example. It's perfectly fine to pass a function in to the constructor even thought the type now is not Unit
since as discussed earlier any value can be discarded and replaced by Unit
. When you evaluate the Unit value, there are no side effects this time since you don't run the method to get the unit, just create a function which is the discarded without being run. def fire = act
When you change the type to be a function.
class Button( act: () => Unit) {def fire = act()}
now only a () => Unit
is a valid argument instead of anything, and the fire method runs that function.
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