The ML module system stands as a high-water mark of programming language support for data abstraction. 然而,从表面上看,似乎可以很容易地在支持抽象类型成员的面向对象语言中进行编码。例如,我们可以在Scala中编码SML模块系统的元素,如下所示:
是否存在任何重要功能,例如编码会错过?任何可以在SML模块中表达的编码都无法表达的东西? SML使得这种编码无法做出任何保证吗?
答案 0 :(得分:49)
您无法轻易克服一些基本差异:
ML签名是结构类型,Scala特征是名义上的:事实之后,任何适当的模块都可以匹配ML签名,对于Scala对象,您需要在定义时声明关系。同样,ML签名之间的子类型是完全结构化的。 Scala 细化更接近结构类型,但有一些相当严重的限制(例如,它们不能引用它们自己的本地类型定义,也不包含对其范围之外的抽象类型的自由引用)。
< / LI> ML签名可以使用include
和where
在结构上进行组合。得到的签名等效于相应签名表达式或类型方程的内联扩展。 Scala的mixin组合虽然在许多方面更强大,但也是名义上的,并且创造了一种不等价的类型。甚至组合的顺序对于类型等价也很重要。
ML仿函数由结构参数化,因此通过类型和值,Scala的泛型类仅按类型进行参数化。要对仿函数进行编码,您需要将其转换为通用的函数,它将类型和值分开。通常,这种转换 - 在ML模块文献中称为相分裂 - 不能仅限于仿函数的定义和用法,因为在它们的调用站点,它必须递归地应用于嵌套结构论点;这最终要求所有结构始终是相位分割的,这不是您想要手动编程的样式。 (也不可能将函数映射到Scala中的普通函数,因为函数无法表达参数和结果类型之间的必要类型依赖性。编辑:自2.10起,Scala支持依赖方法,可以编码SML的一阶生成函子的一些例子,虽然在一般情况下似乎不可能。)
ML有一个改进和传播“半透明”类型信息的一般理论。 Scala使用较弱的“路径依赖”类型的等式理论,其中路径表示对象。因此,Scala使用对象(具有类型成员)作为第一类值的能力来交换ML更具表现力的类型等价。如果不快速遇到可判定性或健全性问题,就不能轻易拥有两者。
编辑: ML可以自然地表达抽象类型构造函数(即更高类型的类型),这通常与仿函数一起出现。对于Scala,必须明确激活更高种类,这对于其类型系统更具挑战性,并且显然会导致不可判断的类型检查。
当您超越SML,更高阶,一流或递归模块时,差异变得更加有趣。我们在MixML paper的第10.1节中简要讨论了一些问题。