我在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)发布于 2015-07-19 10:15:36
调用(F)是期望一个函数可以接受一个A(或任何子类型)并返回一个A(或任何子类型)。原因1和2工作是因为您的函数定义可以接受A或A的任何子类型,而在1中,返回A(满足A的条件)或在2中返回B(满足A的子类型的条件)。
后两者不能工作的原因是,您传入一个只能接受B并返回A或B的函数,为了了解为什么这是一个问题,假设您有类C扩展。B2A将无法处理C,这意味着,根据定义,调用(F)将无法处理输入C,尽管它是A的一个子类型。这是一个编译错误,原因是调用的定义。
发布于 2015-07-19 10:24:26
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是结果类型(协变量):A2B时,可以返回AB是参数的类型(B2A和B2B)时,不能接受A,但是可以传递超级A类型,如Any (Any2A和Any2B)。关于Function的反方差(和协方差)的一些解释可以在http://www.artima.com/pins1ed/type-parameterization.html#19.6中找到。
https://stackoverflow.com/questions/31500053
复制相似问题