In Scala, why can't I partially apply a function without explicitly specifying its argument types?

This produces an anonymous function, as you would expect (f is a function with three arguments):

f(_, _, _)

What I don't understand is why this doesn't compile, instead giving a "missing parameter type" error:

f(_, _, 27)

Instead, I need to specify the types of the underscores explicitly. Shouldn't Scala be able to infer them given that it knows what the function f's parameter types are?

Answers


References below are to the Scala Language Specification

Consider the following method:

def foo(a: Int, b: Int) = 0

Eta Expansion can convert this to a value of type (Int, Int) => Int. This expansion is invoked if:

a) _ is used in place of the argument list (Method Value (§6.7))

val f = foo _

b) the argument list is omitted, and expected type of expression is a function type (§6.25.2):

val f: (Int, Int) => Int = foo

c) each of the arguments is _ (a special case of the 'Placeholder Syntax for Anonymous Functions' (§6.23))

val f = foo(_, _)   

The expression, foo(_, 1) doesn't qualify for Eta Expansion; it just expands to (a) => foo(a, 1) (§6.23). Regular type inference doesn't attempt to figure out that a: Int.


If you are thinking about partial application, I thought that this was only possible with multiple parameter lists (whereas you only have one):

def plus(x: Int)(y: Int) = x + y //x and y in different parameter lists

val plus10 = plus(10) _ //_ indicates partial application

println(plus10(2)) //prints 12

Your example is interesting though as I was completely unaware of the syntax you describe and it appears you can have partial application with a single parameter list:

scala> def plus2(x: Int, y: Int) = x + y
plus2: (x: Int,y: Int)Int

scala> val anon = plus2(_,_)
anon: (Int, Int) => Int = <function2>

scala> anon(3, 4)
res1: Int = 7

So the compiler can clearly infer the type Int!

scala> val anon2 = plus2(20,_)
<console>:5: error: missing parameter type for expanded function ((x$1) => plus2(20, x$1))
       val anon2 = plus2(20,_)
                            ^

Hmmm, strange! I don't seem to be able to do partial application with a single parameter list. But then if I declare the type of the second parameter, I can have partial application!

scala> val anon2 = plus2(20,_: Int)
anon2: (Int) => Int = <function1>

scala> anon2(24)
res2: Int = 44

EDIT - one thing I would observe is that it seems like the following two shortenings are equivalent, in which case it's a bit more obvious that this is not a "partial application" but more like a "function pointer"

val anon1 = plus2(_,_)
val anon2 = plus2 _

I think it is because overloading makes it impossible for the compiler to infer the types:

scala> object Ashkan { def f(a:Int,b:Int) = a; def f(a:Int,b:String) = b; }
defined object Ashkan

scala> Ashkan.f(1,2)
res45: Int = 1

scala> Ashkan.f(1,"Ashkan")
res46: String = Ashkan

scala> val x= Ashkan.f _
<console>:11: error: ambiguous reference to overloaded definition,
both method f in object Ashkan of type (a: Int, b: String)String
and  method f in object Ashkan of type (a: Int, b: Int)Int
match expected type ?
       val x= Ashkan.f _
                     ^

scala> val x= Ashkan.f(_,_)
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => Ashkan.f(x$1, x$2))
       val x= Ashkan.f(_,_)
                       ^
<console>:11: error: missing parameter type for expanded function ((x$1: <error>, x$2) => Ashkan.f(x$1, x$2))
       val x= Ashkan.f(_,_)
                         ^

scala> val x= Ashkan.f(_,"Akbar")
<console>:11: error: missing parameter type for expanded function ((x$1) => Ashkan.f(x$1, "Akbar"))
       val x= Ashkan.f(_,"Akbar")
                       ^

scala> val x= Ashkan.f(1,_)
<console>:11: error: missing parameter type for expanded function ((x$1) => Ashkan.f(1, x$1))
       val x= Ashkan.f(1,_)
                         ^

scala> val x= Ashkan.f(1,_:String)
x: String => String = <function1>

I feel this is one of those border cases arising from all the code conversion, since this involves the creation of an anonymous function which directs the call to the original method. The type is for the argument of the outer anonymous function. In-fact, you can specify any sub-type i.e

val f = foo(_: Nothing, 1) 

even this would compile


Need Your Help

iOS 8 Core Bluetooth not discovering peripherals

ios iphone bluetooth core-bluetooth

I'm having trouble getting Core Bluetooth to discover peripherals on iOS 8. Same code works fine on iOS 7 device. Initially I thought it would be a permissions issue since I had been doing some iBe...

ggplot2 error:Discrete value supplied to continuous scale R

r ggplot2 continuous geomap

I have a dataset called "alldata", which contains 1000 rows and 2 columns named "day_of_Week" and "label". The dataset look like this :