我偶然发现了Scala中的一些让我感到困惑的事情。似乎高阶函数的参数类型不是多态的。以下是我的意思:
class A
class B extends A
def call(f: A => A):A = f(new A)
def A2A(a: A): A = new A
def A2B(a: A): B = new B
def B2A(b: B): A = new A
def B2B(b: B): B = new B
call(A2A) // Works
call(A2B) // Works
call(B2A) // Error!
call(B2B) // Error!
两个调用的错误消息相同:
<console>:12: error: type mismatch;
found : B => A
required: A => A
call(new B, B2A)
<console>:12: error: type mismatch;
found : B => B
required: A => A
call(new B, B2B)
A2A和A2B工作,因此它不是导致问题的返回类型,它必须是参数类型。这与类型擦除有关吗?
我不认为我正在尝试做的事情有任何逻辑上的错误(可能有吗?)所以我怀疑这是类型系统的怪癖?
修改
感谢您的回答!事实证明,这毕竟是我错误的逻辑。问题在于call
的定义。它期望一个函数以A
(或子类型)作为参数。
B2A
和B2B
都采用B
(或子类型),因此与call
的定义不兼容。
我通过引入A
从被调用的ALike
扩展的特征并更改了call
的定义来解决了这个问题,因此它将ALike
的任何参数作为超类型。
trait ALike
class A extends ALike
class B extends A
def call[T <: ALike](t: T, f: T => A): A = f(t)
def A2A(a: A): A = a
def A2B(a: A): B = new B
def B2A(b: B): A = new A
def B2B(b: B): B = b
call(new A, A2A)
call(new A, A2B)
call(new B, B2A)
call(new B, B2B)
答案 0 :(得分:3)
call(f)期望一个可以接受A(或任何子类型)并返回A(或任何子类型)的函数。原因1和2的作用是你的函数定义可以取A或A的任何子类型,并且在1中,返回A(满足条件为A)或2,返回B(满足作为A的子类型的条件) )。
后两者不工作的原因是你传递了一个只能拿B并返回A或B的功能。要想知道为什么这是一个问题,假设你&#39;已经得到C类扩展A. B2A无法处理C,这意味着,根据定义,调用(f)无法处理输入C,尽管是子类型一个。由于调用的定义,这是一个编译错误。
答案 1 :(得分:3)
Scala Function
的参数是逆变的。在A => A
的情况下,这意味着您可以创建一个接受超级类型A
但不接受像B
这样的子类型的函数的函数。
您可以定义一个需要Any
的函数:
def Any2A(any: Any) = new A
def Any2B(any: Any) = new B
call(Any2A) // A = A@b5bddc9
call(Any2B) // A = B@62c1c65f
功能1 [-T1,+ R]
Function1
的参数是逆变的,结果是协变的,这意味着在A => A
类型的函数中:
B
为结果类型(协变)时,您可以返回A
:A2B
B
是参数的类型(A
和B2A
)时,您无法接受B2B
,但您可以传递超级类型{{ 1}}喜欢A
(Any
和Any2A
)。关于Any2B
的逆变(和协方差)的一些解释可以在Programming in Scala, 1ed