Polarity [blog/lti/polarity]
Polarity [blog/lti/polarity]
为了计算关于 $R$ 的具体类型参数,还有一个至关重要的操作:变量代换导致的极性变化。 一个类型变量 $X$ 在另一个类型表达式 $R$ 中的极性,描述了当该变量的类型变大或变小时,整个表达式的类型会如何相应地变化。
- 协变 (Covariant):若 $R$ 在 $X$ 上是协变的,则 $X$ 的「变大」(成为一个超类型)会导致 $R$ 也「变大」。例如,在 $T \to X$ 中,$X$ 是协变的。若 $\mathbb{Z} \lt: \mathbb{R}$,则 $T \to \mathbb{Z} \lt: T \to \mathbb{R}$。
- 逆变 (Contravariant):若 $R$ 在 $X$ 上是逆变的,则 $X$ 的「变大」会导致 $R$ 「变小」(成为一个子类型)。例如,在 $X \to T$ 中,$X$ 是逆变的。若 $\mathbb{Z} \lt: \mathbb{R}$,则 $\mathbb{R} \to T \lt: \mathbb{Z} \to T$。
- 不变 (Invariant):若 $R$ 在 $X$ 上是不变的,则只有当 $X$ 保持不变时,$R$ 才保持不变。任何改变都会导致不可比较的结果。例如,在 $X \to X$ 中,$X$ 是不变的。
- 常量 (Constant):若 $R$ 在 $X$ 上是常量的,则 $X$ 的变化不会影响 $R$ 的类型。常量类型通常是指那些不包含变量的类型,如基本类型或具体的类。
现在我们对极性的考虑主要集中在函数类型之上, 只需要关注变量在函数类型中的位置即可(在箭头的左边还是右边), 当然,需要考虑到嵌套的函数结构。建议读者在此稍作停顿,考虑一下该算法的设计。 当然你也可以直接展开下面的代码块来查看具体实现。
1. Variance Code [blog/lti/variance_code]
pub fn variance(self : Type, va : Var) -> Variance {
letrec go = (ty : Type, polarity : Bool) => match ty {
TyVar(v) if v == va => if polarity { Covariant } else { Contravariant }
TyVar(_) | TyTop | TyBot => Constant
TyFun(_, params, ret) => {
let param_variance = params
.map(p => go(p, !polarity))
.fold(init=Constant, combine_variance)
combine_variance(param_variance, go(ret, polarity))
}
}
and combine_variance = (v1 : Variance, v2 : Variance) => match (v1, v2) {
(Constant, v) | (v, Constant) => v
(Covariant, Covariant) | (Contravariant, Contravariant) => v1
(Covariant, Contravariant) | (Contravariant, Covariant) => Invariant
(Invariant, _) | (_, Invariant) => Invariant
}
go(self, true)
}