Reputation: 7207
Having a function f
defined as:
def f(i1: Int, i2: Int)(i3: Int) = i1 + i2 + i3
It is possible to define a partially applied function as follows using _
:
val f12 = f(1, 2) _ // f12: Int => Int = <function>
f12(3) // res0: Int = 6
Now when I return a partially applied function from a function, I do not need to use _
:
def f23(f: (Int, Int) => Int => Int) = f(2, 3) // f23: (f: (Int, Int) => Int => Int)Int => Int
val f23f = f23(f) // f23f: Int => Int = <function>
f23f(4) // res1: Int = 9
If I place _
at the f23
definition I will get an Error:
def f23(f: (Int, Int) => Int => Int) = f(2, 3) _
Error:(6, 49) _ must follow method; cannot follow Int => Int
def f23(f: (Int, Int) => Int => Int) = f(2, 3) _
What is the reason for this inconsistency?
Upvotes: 2
Views: 296
Reputation: 51271
f
is a method, not a function. You can read about some of the differences here.
f12
is a function derived from f
via eta-expansion. It is not a partial function. A PartialFunction
is a function defined over a limited domain of input values. If, say, f12
was defined only for Int
values less than 500, for example, and undefined for input values greater than 500, then it would be a partial function.
def f23(f: (Int, Int) => Int => Int) = f(2, 3) _
This fails because f
, as defined here, is a function that takes 2 Int
values and returns a function that takes an Int
and returns an Int
. In this situation what is the underscore supposed to be standing in for? f(2,3)
is a complete invocation that returns an Int=>Int
function. It's a bit like writing 5 + 7 _
. It's not clear what the _
is substituting for.
You can, on the other hand, do this: ... = f(2,3)(_)
. Then it's clear that the returned function is being invoked with a missing, i.e. _
, parameter. Which is the same thing as: ... = f(2,3)
.
Upvotes: 3
Reputation: 855
This is by design in Scala to prevent developer confusion. If you tell the compiler the type of f12 explicitly it will work as you expect:
`val f12:Int=>Int = f(1, 2)`
This is explained by the language originator (Martin Odersky):
Why the trailing underscore?
Scala's syntax for partially applied functions highlights a difference in the design trade-offs of Scala and classical functional languages such as Haskell or ML. In these languages, partially applied functions are considered the normal case. Furthermore, these languages have a fairly strict static type system that will usually highlight every error with partial applications that you can make. Scala bears a much closer relation to imperative languages such as Java, where a method that's not applied to all its arguments is considered an error. Furthermore, the object-oriented tradition of subtyping and a universal root type accepts some programs that would be considered erroneous in classical functional languages.
For instance, say you mistook the drop(n: Int) method of List for tail(), and you therefore forgot you need to pass a number to drop. You might write, "println(drop)". Had Scala adopted the classical functional tradition that partially applied functions are OK everywhere, this code would type check. However, you might be surprised to find out that the output printed by this println statement would always be ! What would have happened is that the expression drop would have been treated as a function object. Because println takes objects of any type, this would have compiled OK, but it would have given an unexpected result.
To avoid situations like this, Scala normally requires you to specify function arguments that are left out explicitly, even if the indication is as simple as a `_'. Scala allows you to leave off even the _ only when a function type is expected.
Upvotes: 1