Subtyping
row type 是一种结构化类型, 也称静态鸭子类型.
如果类 A 中所有的 row 另一个类 B 全部都有, 那么我们称 A <: B
.
和 A, B 是否是继承实现的完全没有关系, 无论 B 是传统的继承类型, 还是继承的原始类型, 还是匿名类型都成立.
说会继承, 生成子类型最常规的方法就是继承.
类继承分为实继承和虚继承
- 实继承: 父类存为成员变量, 接口不需要重新实现, 调用直接转发给父类即可.
- 虚继承: 父类不存在, 接口需要重新实现.
实继承可以带访问修饰符来决定成员变量的访问权限
- private: class level, 只能类内部访问
- protect: package level, 只能同级模块和子模块访问
- internal: package level, 只能包内部访问
- public: 可以任意访问
举个例子, 如下继承关系
(A, B)
(virtual A, public B) ;
编译期展开后为
Method Resolve Order
你看这里出现了多继承多继承, 我们需要解析方法的调用顺序, 特别是菱形继承的情况下.
解析需要遵从三个原则 (Consistent)
- 扩展一致性原则
- 局部优先原则
- 单调性原则
满足这三个原则的算法就是 C3 线性化算法, 解析结果称为方法解析序(MRO, Method Resolution Order).
比如说这个图
(object)
(object)
(object)
(object)
(object)
(C, A, B)
(B, D, E)
(A, D)
(K1, K3, K2)
其 mro 应为 [Z, K1, C, K3, A, K2, B, D, E, object]
可以发现, mro 其实就是 row 这里面同名 row 的排列顺序.
Type Class
我们可以定义一些不参与 mro 排序的类, 这些类的 row 指针永远指向最上方.
: ToString {
def to_string(self): str
}
(A)
// MRO = B, A
typeof(B) =
let b = B();
B::to_string(b) // "B"
ToString::to_string(b) // "B"
A::to_string(b) // "A"
这里的 ToString
实质上就是起到了 type class 的效果.
等会儿, ToString::to_string
指向最上方返回 "B" 我能理解, 为什么还能调用 A::to_string
返回 "A" 呢?
这是因为 B 代理了 A 的指针, 在 scoped row type 系统里会自动转发.
如果要禁掉这种转发那就用虚继承即可.
这告诉我们, 还缺一套精确控制 row 转发的机制.
Method Resolve Qualifier
在类型层面, 简单起见, 我们把字段属性看成方法的一种, 我们不区分以下两者:
field: T
field(): T
实继承的情况下, 方法可以有如下几种修饰词
- _: 不写, 默认转发父类同名方法
- inherit: 手动转发
- virtual: 无法调用, 子类必须重写, 除非子类是虚基类
- override: 重写父类方法, 除非父类方法是 final 方法
- final: 原则上禁止重写
来看一些例子, 我这里加上 123 的编号省的分不清, 实际上通过 & 附加的时候不带编号
type A = {
inherit a1: str
virtual a2: str
// A::a 报错, a 是虚方法无法调用
}
type B =
type C = {
override c1: str
virtual c2: str
// C::c 返回 c1
// C2::c 报错, c1 是虚方法无法调用
}
可以发现一虚皆虚, 编译期可以直接干掉虚方法后面的所有 row.