在Scala中,如果不显式地指定函数的参数类型,为什么不能部分应用函数呢?

时间:2021-01-05 22:33:17

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

这将产生一个匿名函数,正如您所期望的(f是一个具有三个参数的函数):

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?

相反,我需要明确地指定下划线的类型。如果Scala知道函数f的参数类型,难道不应该推断它们吗?

4 个解决方案

#1


17  

References below are to the Scala Language Specification

下面引用的是Scala语言规范

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:

Eta展开可以将其转换为类型(Int, Int) => Int的值。

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

一)_中使用的参数列表(方法值(§6.7))

val f = foo _

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

b)省略参数列表,和预期类型的表达式是一个函数类型(§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))

c)的每个参数是_(的一个特例的占位符语法匿名函数(§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.

表达式,foo(_, 1)不符合Eta展开;只是扩展(a)= > foo(a,1)(§6.23)。常规类型推断不试图找出a: Int。

#2


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!

所以编译器可以清楚地推断出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 _

#3


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>

#4


-3  

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

即使这将编译

#1


17  

References below are to the Scala Language Specification

下面引用的是Scala语言规范

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:

Eta展开可以将其转换为类型(Int, Int) => Int的值。

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

一)_中使用的参数列表(方法值(§6.7))

val f = foo _

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

b)省略参数列表,和预期类型的表达式是一个函数类型(§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))

c)的每个参数是_(的一个特例的占位符语法匿名函数(§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.

表达式,foo(_, 1)不符合Eta展开;只是扩展(a)= > foo(a,1)(§6.23)。常规类型推断不试图找出a: Int。

#2


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!

所以编译器可以清楚地推断出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 _

#3


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>

#4


-3  

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

即使这将编译