(强调我的)
但是,如果重新定义字段和函数类型协变没有问题,那么重新定义参数的类型协变怎么会导致麻烦呢?
协变重新定义等于子类型化,对吧?子类型可以代替它们的超类型!
有什么问题?
(强调我的)
但是,如果重新定义字段和函数类型协变没有问题,那么重新定义参数的类型协变怎么会导致麻烦呢?
协变重新定义等于子类型化,对吧?子类型可以代替它们的超类型!
有什么问题?
问题不在于协方差本身。(特别是,如果它是逆变的,那么按合同设计是不可能的,因为后代类的特征中的参数类型不必在其父类中具有可用的特征。使用协方差就不会有这样的问题。)
有问题的是协方差与多态性的结合。例如
class A feature
foo (a: A) do a.bar end -- (1)
bar do end
end
class B inherit A redefine foo end feature
foo (a: B) do a.qux end -- (2)
qux do end
end
现在下面的代码会崩溃:
a: A; b: B
...
create b
a := b
a.foo (create {A})
实际上,a.foo
会调用版本 (2),因为a
它附加到类型为 的对象B
。但是,传递给此功能的参数将是类型A
。并且A
没有qux
导致运行时错误的功能。这种错误称为 CAT 调用(更改可用性或类型)。
解决这个问题的方法是避免将协变与多态一起使用,即调用不应该是多态的,或者不应该对参数进行协变重新声明。该解决方案的工作正在进行中。
“调用不应该是多态的,或者不应该有参数的协变重新声明。”
你怎么知道?
让我们稍微改变一下你的例子:
嗡嗡声(a_a:A)做a_a.foo(创建{A})结束
这看起来很无辜。但是如果buzz 接收到一个动态类型B 的参数,你仍然会得到catcall。嗡嗡声的作者很可能处于未知B存在的情况。
我认为你需要放弃“一个电话不应该是多态的或”的建议。简单地禁止参数的协变重新声明。