2
def sum(f: Int => Int): (Int, Int) => Int = {
    def sumF(a: Int, b: Int): Int =
        if (a > b) 0
        else f(a) + sumF(a + 1, b)
    sumF
}

def sumCubes    = sum(a => a * a * a)


sumCubes // output Function2
sumCubes(1,10) // output 3025.
sumCubes() // doesn't work

I feel I dont understand currying well enough. In the first statement , we are calling sumCubes without parameters , hence sum gets called with the anonymous function as parameter and returns a function2.

Whats really happening in 2nd and 3rd invocation , Why are we able to do

sum(a => a * a * a)(1,10)

but not

sumCubes()(1,10)

My understanding is that in sum(a => a * a * a)(1,10) , we are partially applying sum to the anonymous function, which returns a Function2 ,which is applied to the second pair of parameters (1,10) and hence we are getting 3025,

However the same should happen in case of sumCubes()(1,10) , invoking sumCubes without parameters first , would inturn invoke sum with the anonymous function and the Function2 returned would be applied to (1,10)

Why does sumCubes(1,10) work but not sumCubes()(1,10) , shouldn't sumCubes and sumCubes() mean the same thing , invocation of function sumCubes. Also if a simple reference sumCubes is invoking it , how can I pass it around. I feel like I am not understanding something fundamental about Scala.

pedrorijo91
  • 7,635
  • 9
  • 44
  • 82
Rpant
  • 974
  • 2
  • 14
  • 37
  • 1
    You ["apply **function** to an **argument**"](https://en.wikipedia.org/wiki/Function_application), not the other way round. In your text, everything seems reversed and backward, e.g. *"we are partially applying the anonymous function to sum"* does not make any sense - you apply `sum` to the anonymous function, not the other way round. – Andrey Tyukin Aug 17 '18 at 19:44

1 Answers1

7

Scala's methods can have multiple argument lists.

For example, here is a method foo that has ten argument lists, of which the first seven are empty:

def foo()()()()()()()(a: Int)(b: Int)(c: Int): Int = a + b + c

You can invoke it as follows:

println(foo()()()()()()()(1)(20)(300))

and it will print 321.

Note that when you invoke a method, the number of argument lists, as well as the number of arguments in each list (and also their types) have to match the declaration.

Another example. The following method has two argument lists:

def bar(i: Int)(f: Int => Int) = f(i)

you can invoke it as follows:

bar(42)(x => x * x)

but not as

bar()(x => x * x)
bar()(42)(x => x * x)
bar(42)()

or anything like it.

Completely analogously, if you have defined a method with zero argument lists

def baz = (x: Int) => x * x

then you must invoke it with zero argument lists too:

baz

Since it returns an Int => Int function, you can of course apply the result to an Int:

baz(42)

which is the same as (baz)(42), but you cannot do the following:

baz()(42)

because baz itself has no argument lists, and () does not contain a single integer argument.


Note that all of the above is actually a simplification: under certain circumstances, methods that have an empty argument list can be called without parentheses, i.e. def foo(): Unit = ... can be invoked as foo, without (). This is a somewhat strange feature, and I can't say exactly why it's there. My best guess would be: it has something to do with java-interop, where you really want to omit parentheses on zero-ary getters.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • 2
    To elaborate a tiny bit on zero-ary methods: the convention is that simple getters have no argument lists, but methods that are more complex or have some side-effect should have the parentheses. The reason you can call one of these without parentheses is merely for java-interop while still maintaining this convention. For example, the most common cases are probably `.toString` and `.hashCode`. In fact dotty plans to change this to make java interop and backwards compatibility the *only* places where this is allowed: https://dotty.epfl.ch/docs/reference/dropped/auto-apply.html – Joe K Aug 17 '18 at 21:04