Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can't apply just an underscore to first parameter in Scala?

Tags:

scala

I don't know why pattern d is bad in this list below. Why need expicit type declaration?

def adder1(m:Int,n:Int) = m + n

val a = adder1(2,_) //OK
val b = adder1(_,2) //OK

def adder2(m:Int)(n:Int) = m + n

val c = adder2(2)(_)     //OK
val d = adder2(_)(2)     //NG:missing parameter type
val e = adder2(_:Int)(2) //OK

I just want to know the reason pattern d needs parameter type. Very welcome just showing citation language spec.

like image 435
watopin Avatar asked Mar 13 '20 10:03

watopin


People also ask

What does underscore do in Scala?

Scala allows the use of underscores (denoted as '_') to be used as placeholders for one or more parameters. we can consider the underscore to something that needs to be filled in with a value. However, each parameter must appear only one time within the function literal.

What does => mean in Scala?

=> is the "function arrow". It is used both in function type signatures as well as anonymous function terms. () => Unit is a shorthand for Function0[Unit] , which is the type of functions which take no arguments and return nothing useful (like void in other languages).

What is a partial function in Scala?

When a function is not able to produce a return for every single variable input data given to it then that function is termed as Partial function. It can determine an output for a subset of some practicable inputs only. It can only be applied partially to the stated inputs.

What does Star mean in Scala?

The _* type annotation is covered in "4.6. 2 Repeated Parameters" of the SLS. The last value parameter of a parameter section may be suffixed by “*”, e.g. (..., x:T ). The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T].


1 Answers

So I believe this comes from the concept of Partial Application.

Intuitively, partial function application says "if you fix the first arguments of the function, you get a function of the remaining arguments"

...

Scala implements optional partial application with placeholder, e.g. def add(x: Int, y: Int) = {x+y}; add(1, _: Int) returns an incrementing function. Scala also support multiple parameter lists as currying, e.g. def add(x: Int)(y: Int) = {x+y}; add(1) _.

Lets take a look at adder2

From the REPL:

scala> def adder2(m:Int)(n:Int) = m + n
def adder2(m: Int)(n: Int): Int

Lets get a value to represent this:

scala> val adder2Value = adder2
                ^
       error: missing argument list for method adder2
       Unapplied methods are only converted to functions when a function type is expected.
       You can make this conversion explicit by writing `adder2 _` or `adder2(_)(_)` instead of `adder2`.

Ok, let's try:

val adder2Value = adder2 _
val adder2Value: Int => (Int => Int) = $Lambda$1382/0x0000000840703840@4b66a923

Ahha!

In English: "A function that takes an Int and returns a function that takes an Int and returns an Int"

How can we bind the second argument using this signature? How can we access the inner function unless we first have gone through the outer one?

As far as I know, this is not possible to do using this signature, unless you explicitly define the type of your first argument.

(But what about adder2(_)(_)?)

scala> adder2(_)(_)
              ^
       error: missing parameter type for expanded function ((<x$1: error>, x$2) => adder2(x$1)(x$2))
                 ^
       error: missing parameter type for expanded function ((<x$1: error>, <x$2: error>) => adder2(x$1)(x$2))

(Maybe this hints at our solution?)

Notice what happens if we explicitly define both arguments:

val adder2Value2= adder2Value (_:Int) (_:Int)
val adder2Value2: (Int, Int) => Int = $Lambda$1394/0x000000084070d840@32f7d983

This is much more manageable, we can now fix either argument, and get a simplified partial function:

scala> val adder2FirstArg = adder2Value (_:Int) (10)
val adder2FirstArg: Int => Int = $Lambda$1395/0x000000084070d040@47f5ddf4

scala> val adder2SecondArg = adder2Value (5) (_:Int)
val adder2SecondArg: Int => Int = $Lambda$1396/0x000000084070c840@21ed7ce

So what's really going on here?

When you bind an argument to a value, you have explicitly expressed the type (maybe it's inferred, but it's definitely that type, in this case, Ints). It's sugar so we don't need to write it. But under the hood, these are composed functions, and how they are composed is very important. To be able to match and simplify the function signature, the compiler requires us to provide this information in an outside-in manner. Otherwise, we need to give it some help to get there.

EDIT:

I think that this question serves as more of a Scala language spec. puzzle exercise, however. I can't think of any good reason, from a design perspective, for which you would need to implement a curried function in such a way that you cannot order the parameters such that the last parameters are the ones being inferred.

like image 84
Randomness Slayer Avatar answered Sep 27 '22 20:09

Randomness Slayer