把未知字段作为参数

行多态的英文名称是Row polymorphism。这里的“行”是类型层面的带标签条目组合,不是数据库中一行数据的复制操作。普通类型变量可以代表某个未知类型,行变量则代表记录类型中尚未列出的字段及其类型。本文以PureScript记录为具体例子,而不把其语法当作所有行类型系统的通则。

可把一个函数的要求写成示意类型“对任意r,{name:String | r}到String”。它只读取字符串字段name,r表示其他字段。仅有name的记录可令r为空;同时含有age或city的记录也可提供相应的r。若缺少name,或name不是要求的字符串类型,则不能靠“还有未知字段”来绕过检查。

参考:[1] [3]

同一行变量连接输入与输出

更重要的用途不只是读取字段,而是处理后保留其他类型信息。设更新函数接受“{age:Int | r}”,并返回“{age:Int | r}”。输入与输出使用同一个r,表示额外字段的类型组合保持对应,不必把所有可能字段逐一写进函数定义。调用者传入带name与city的记录后,仍可在结果类型中使用这些字段。

具体实现若仅以记录更新语法把age加一,就会构造保留其余字段值的新记录。须分清两层保证:相同r表达类型信息的关联,而额外字段值不变还依赖实现的操作。PureScript教材用更新坐标的平移函数展示这种模式,同一函数可处理带宽高的矩形记录与带半径等字段的圆弧记录。

参考:[2]

与封闭记录及子类型的区别

在教材的PureScript例子里,不带行变量、只列出两个姓名字段的函数签名要求恰好那些字段;加上行变量后,函数可接受更多字段。封闭记录与开放记录的区别,因此不是运行时检查得严或松,而是类型表达是否留下可以实例化的剩余部分。

宽度子类型也能让较多字段的记录用在较少字段的上下文中,但其基本规则是允许以较一般类型看待原值。行多态通过参数化的r表达一族相关类型,尤其能在输入与输出间保留额外字段的联系。结构类型关注如何依据结构判断类型关系,行多态关注怎样对未知结构部分作量化;二者相关,却不能互作同义词。

参考:[1] [2] [4]

字段存在与缺失都有规则

字段扩展必须说明遇到同名标签怎么办,不能一律假定行就是无重复的普通字典。PureScript的Prim.Row文档区分了多种类型层运算:Union计算带左偏规则且容许重复的行组合,Nub移除重复标签,Lacks则断言某标签未出现在给定行中。这些区别体现了标签约束是机制的一部分。

例如需要加入一个原先没有的字段时,缺失约束可表达这个前提;已有字段的读取则要求其存在且类型相符。其他系统如何处理遮蔽、重复或缺失,应查看各自规则。仅在运行时用字符串查找对象属性,不等于已经具有行多态,因为那里未必存在经过静态约束、在多个类型位置间传递的行变量。

参考:[3] [1]