开发者

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 b开发者_如何学运维e able to infer them given that it knows what the function f's parameter types are?


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜