1 个不稳定版本

0.0.0 2023年6月20日

#8#c3

MPL-2.0 许可证

14KB
241

子类型化

row类型是一种结构化类型,也称为静态鸭子类型。

如果类A中所有的row在另一个类B中全部都有,那么我们称A <: B

与A和B是否是继承实现完全没有关系,无论B是传统的继承类型,还是继承的原始类型,还是匿名类型都成立。

继承通常是指生成子类型的最常规方法。

类继承分为实继承和虚继承

  • 实继承:父类作为成员变量存储,接口不需要重新实现,调用直接转发给父类即可。
  • 虚继承:父类不存在,接口需要重新实现。

实继承可以带有访问修饰符来决定成员变量的访问权限

  • private:类级别,只能类内部访问
  • protect:包级别,只能同级模块和子模块访问
  • internal:包级别,只能包内部访问
  • public:可以任意访问

例如,如下继承关系

class K1(A, B) { }
class K2(virtual A, public B) { };

编译期展开后为

class K1 {
    private _a: A
    private _b: B
}

class K2 {
    public b: B
}

方法解析顺序

你看这里出现了多继承,我们需要解析方法的调用顺序,尤其是在菱形继承的情况下。

解析需要遵循三个原则(一致性)

  • 扩展一致性原则
  • 局部优先原则
  • 单调性原则

满足这三个原则的算法就是C3线性化算法,解析结果称为方法解析序(MRO,Method Resolution Order)。

比如说这个图

class A(object) {}
class B(object) {}
class C(object) {}
class D(object) {}
class E(object) {}
class K1(C, A, B) {}
class K2(B, D, E) {}
class K3(A, D) {}
class Z(K1, K3, K2) {}

其mro应为[Z, K1, C, K3, A, K2, B, D, E, object]

可以发现,mro其实就是row中同名row的排列顺序。

类型类

我们可以定义一些不参与MRO排序的类,这些类的row指针永远指向最上方。

trait ToString {
    def to_string(self): str
}
class A: ToString {
    def to_string(self): str { "A" }
}
class B(A) {
    private _b: B
    def to_string(self): str { "B" }
}

// MRO = B, A
typeof(B) = {
    B::to_string: (self) -> str,
    A::to_string: (self) -> str,
}

let b = B();

B::to_string(b)        // "B"
ToString::to_string(b) // "B"
A::to_string(b)        // "A"

这里的ToString实质上就是起到了类型类的作用。

等会儿,ToString::to_string指向最上方返回"B",我能理解,为什么还能调用A::to_string返回"A"呢?

这是因为B代理了A的指针,在scoped row type系统中会自动转发。

如果要禁掉这种转发那就用虚继承即可。

这告诉我们,还缺一套精确控制row转发的机制。

方法解析限定符

在类型层面,为了简单起见,我们把字段属性看成方法的一种,我们不区分以下两者

field: T
field(): T

实继承的情况下,方法可以有如下几种修饰词

  • _: 不写,默认转发父类同名方法
  • inherit:手动转发
  • virtual:无法调用,子类必须重写,除非子类是虚基类
  • override:重写父类方法,除非父类方法是final方法
  • final:原则上禁止重写

来看一些例子,我这里加上123的编号省的分不清,实际上通过&和附加的时候不带编号

type A = {
    inherit a1: str
    virtual a2: str
    // A::a 报错, a 是虚方法无法调用
}
type B = {
    override b1: str
    final b2: str
    // B::b 报错, 禁止重写 final 方法
}
type C = {
    override c1: str
    virtual c2: str
    // C::c 返回 c1
    // C2::c 报错, c1 是虚方法无法调用
}

可以发现一虚皆虚,编译期可以直接干掉虚方法后面的所有row。

无运行时依赖