Reputation: 9761
I have the following code
def call1 = 4
call1 _
and get the following warning:
Methods without a parameter list and by-name params can no longer be converted to functions as
m _
, write a function literal() => m
instead
I am not sure to fully grasp why and part of the sentence here. Obviously i do not have a by-name parameter here.
First of all is the warning splitable in two as such:
1)
Methods without a parameter list can no longer be converted to functions as
m _
, write a function literal() => m
instead
by-name params can no longer be converted to functions as
m _
, write a function literal() => m
instead
I am seeking an explanation for 1) and 2). And on point 2, I have a hard time visualising why and when a by-name params would be converted to functions.
Upvotes: 3
Views: 268
Reputation: 27535
Both can be demonstrated by something like:
def needFunction(f: () => String) = {
println("calling function")
println(f())
}
def paramLess: String = {
println("calculating value")
"foo"
}
I want to pass paramLess
into needFunction
. When will it be evaluates?
needFunction(() => paramLess) // (1)
// calling function
// calculating value
// foo
This is obvious - paramLess
will be evaluated inside needFunction
. But this?
needFunction(paramLess _) // (2)
(2)
is the same as (1)
but I needed to take a moment to think how it would behave and run it in REPL to be sure.
This would happen quite often, especially with by-name params - you want to make some API easy to use by the user so you take thunk: => A
as argument, but internally you are passing () => A
to know exactly when you are evaluating (passing one by-name into another by-name and so on hoping that nothing in between evaluates results in some very fragile code).
Functions and methods with parameter don't have readability issues - if you don't put ()
anywhere it is not evaluated. You can compose it, pass it, return it - but if there is no argument passed, there is no evaluation. Paramless methods evaluate every time you use their name, so they are tricky if they perform side-effects or some expensive computations - that however is avoided and discouraged, as you are suggested to add at least ()
parameter list to side-effecting method. At the same time, they kind of pretend to be values, which don't need arguments. Apparently, here community decided that if they want to pretend to be values, they should be used as values, with () => value
as the way of creating a function - makes sense if we will not use them for side-effects.
By-name params are a similar case plus when it comes to readability, but - from what I saw - they have a lot of corner cases resulting in bugs when combined with eta-expansion and they also quite often (almost always?) appear in the context of side effects
def inSomeContext(context: Arguments)(thunk: => SideEffects) = ...
// Try.apply
// IO.apply
// option.fold(noneByName)(some => ...)
// ...
This makes them even more dangerous than paramless methods (where side-effects are discouraged) so a warning is even more justified.
The bottom line is that it is mostly about readability, safety and avoiding surprises:
val
ues (initialization order might be one reason) without side effects and surprises (there are more warnings that warns against side-effects in paramless methods),() => A
kind of function) or value (val evaluated = thunk
) that you would pass on safely.Warning against other usages of these help keep the code readable and without bad surprises.
Upvotes: 5