【问题标题】:Scala: Polymorphism in argument type of higher order functionScala:高阶函数参数类型的多态性
【发布时间】:2015-07-19 19:55:12
【问题描述】:

我在 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(或子类型)作为参数的函数。

B2AB2B 都采用 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)

【问题讨论】:

  • 我认为您在编辑中输入了B2A 的结果类型。应该是def B2A(b: B): A = new A

标签: scala functional-programming


【解决方案1】:

call(f) 期待一个可以接收 A(或任何子类型)并返回 A(或任何子类型)的函数。 1 和 2 起作用的原因是您的函数定义可以采用 A 或 A 的任何子类型,并且在 1 中,返回 A(满足作为 A 的条件)或在 2 中,返回 B(满足作为 A 的子类型的条件)。

后两者不起作用的原因是您传入的函数只能接受 B 并返回 A 或 B。要了解为什么这是一个问题,假设您有 class C extends A . B2A 将无法处理 C,这意味着 call(f) 根据定义将无法处理输入 C,尽管是 A 的子类型。由于 call 的定义,这是一个编译错误。

【讨论】:

  • 啊,所以这是我的错误逻辑,而不是类型系统的怪癖
【解决方案2】:

Scala Function 的参数是逆变的。在A =&gt; 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 =&gt; A类型的函数中:

  • A 是结果类型(协变)时,您可以返回BA2B
  • A 是参数的类型(B2AB2B)时,您不能接受B,但您可以传递A 的超类型,例如Any (@987654340 @ 和 Any2B)。

关于Function的逆变(和协方差)的一些解释可以在Programming in Scala, 1ed中找到

【讨论】:

  • 感谢您的回答,我选择限制类型而不是使用 Any(请参阅我的编辑)。
  • 我刚刚使用Any 来表明您可以使用超类型作为参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 2019-01-25
相关资源
最近更新 更多