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

后端 未结 4 1464
余生分开走
余生分开走 2020-12-04 21:48

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

f(_, _, _)

What I don\'t understand is why

相关标签:
4条回答
  • 2020-12-04 22:18

    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>
    
    0 讨论(0)
  • 2020-12-04 22:30

    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.

    0 讨论(0)
  • 2020-12-04 22:31

    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 讨论(0)
  • 2020-12-04 22:34

    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 _
    
    0 讨论(0)
提交回复
热议问题