35

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?

vt.
  • 1,073
  • 8
  • 12
  • 4
    Can you give an actual code example of what you are trying to do? You say this "doesn't compile" - *in what context?* – oxbow_lakes Mar 02 '10 at 12:42
  • Thanks for your answer. I'm not actually using this sort of code fragment in practice but I came across the problem simply while playing with Scala's placeholder syntax. The latter part of your answer pretty much describes what I was trying to do. – vt. Mar 02 '10 at 13:26
  • 3
    Seems like a arbitrary limitation of the type inferencer implementation. The truth lies somewhere in here: http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/compiler/scala/tools/nsc/typechecker/Infer.scala – retronym Mar 02 '10 at 13:30
  • @vt - don't feel you can't upvote me just because I didn't answer the question! Pfft :-) – oxbow_lakes Mar 02 '10 at 13:40

4 Answers4

19

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.

retronym
  • 54,768
  • 12
  • 155
  • 168
  • 10
    "Regular type inference doesn't attempt to figure out that a: Int.". And may I ask, Why ?! – Ashkan Kh. Nazary Oct 11 '12 at 16:42
  • 3
    Type inference of anon function parameters, e.g. the `a` in `(a) => foo(a, 1)`, is only based on the expected type, not on the usages of the parameter in the body of the function. – retronym Oct 12 '12 at 07:30
  • 1
    I guess what you're asking, though, is could the rules be changed so that `foo(_, 1)` is expanded to `(a: Int) => foo(a, 1)`, perhaps assuming that there is only one overload of `foo` that matches the arity and the other arguments. While it sounds easy enough, I think it would add significant additional complexity to the spec and implementation. – retronym Oct 12 '12 at 07:33
8

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 _
oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • 3
    `val f = foo(_, _)` expands to `val f = (a, b) => foo(a, b)`, it isn't precisely the same as partially applying a curried function. – retronym Mar 02 '10 at 13:32
  • But it *seems* to have the same effect, you must admit. And if the compiler can infer types with 2 `_` placeholders, why does it have trouble when there is only one? – oxbow_lakes Mar 02 '10 at 13:39
  • 2
    To answer that question, you could debug through Infer.scala in the compiler :) I agree, it seems like an unnecessary limitation, but scala's type inference is intentionally omitted from the spec to allow such wiggle room. – retronym Mar 02 '10 at 14:24
  • 3
    Partial application does not require curried methods, it is applicable to any method or function. – Randall Schulz Mar 02 '10 at 14:34
  • @Randall - indeed! I was not aware of this before this question came up – oxbow_lakes Mar 02 '10 at 14:43
1

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>
Ashkan Kh. Nazary
  • 21,844
  • 13
  • 44
  • 68
-4

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

Nagaraj
  • 131
  • 4