1.3. 模块

SML’97的SML模块系统发生了重大变化。此更改的动机是简化模块化系统,主要是通过削弱结构共享的概念,并通过添加定义来提高签名的表达能力类型规范。增加了定义型规范对类型共享规范的新限制。在许多情况下共享规范本应在SML’90中使用,而在SML'97中则是使用定义类型是必要的,或者可能只是更好的风格规范。在SML'97中,结构没有唯一的、可检查的静态身份和结构共享被视为缩写用于隐式类型共享。缺少结构级等效的很快发现定义型规格很难,SML/NJ实现了这个有用的功能(第1.6.2节).

1.3.1. 在签名中键入缩写

SML’97定义,第G.1节。

人们早就意识到SML的原始签名语言出于某些目的,90年代的表现力不够。特别是,它通常不可能同时表明存在和规范中类型组件的定义。在特殊情况下,可以使用定义的类型共享规范,如

签名S=信号发生器类型t共享类型t=int结束
但如果t吨的定义是一种类型表情似的int列表所以在97年的SML中类型规范的定义扩展为允许表格规格:
    类型 tyvarseq泰康=第y天
我们将这种规范称为定义规范(同时有时称为类型缩写规范). 使用此按照某种规范,我们可以更整洁地重写上述签名如下:
签名S=信号发生器类型t=int列表结束
允许在签名中使用类型定义规范,可以编写一个更全面地捕获或约束类型的签名结构中的信息。它还使其更实用下面描述的签名匹配的不透明形式(第节1.3.9),因为可以更好地控制哪些导出类型是抽象的(不透明的)那些是混凝土的(透明的).

SML/NJ从版本开始引入了类型定义规范0.93,它们也在Caml Special Light和奥卡姆(?)。SML’97的主要改进是减少混淆类型定义规范和类型共享之间的交互specs通过对类型共享specs施加新的限制。我们限制可以在共享规范中显示的类型与在中指定的类型当前签名(第节1.3.4),我们禁止定义共享规范(第节1.3.5). 我们还增加了使用“其中类型“语法到将定义附加到先前定义的签名(第节1.3.3). 最终的设计为我们提供了更多表达能力超过SML’90。

引入类型定义规范的动机也适用于结构规范。在SML’90程序中,我们经常使用定义结构共享规范,这种形式的共享规范是SML'97是不允许的。显然是类型定义的结构模拟规格将是一个方便的替代品,但不幸的是SML'97没有提供它们。因此,SML/NJ填补了这一空白,如下所述关于结构定义规范的章节(第1.6.2节).

1.3.2. 数据类型复制规范

中描述了数据类型复制声明第1.1.5节.一个提供了类似的数据类型复制规范表对于签名,它具有与声明相同的语法:

    数据类型 典型值=数据类型 长子
数据类型复制规范与数据类型规范的作用相同正如定义类型规范对简单类型规范所做的那样,我们使用学期定义规范涵盖这两种形式。下面是一个示例
结构A=结构数据类型'a t=C|D of'a*'a t结束签名S=信号发生器数据类型t=数据类型A.t结束
请注意,尽管A.t公司(因此t吨)是一元类型构造函数,我们不包括形式类型变量数据类型复制规范中的参数。此规范的效果是定义类型组件t吨与…相同A.t公司此外,它还隐式指定数据构造函数C类D类与关联A.t公司。因此,在functor声明中,例如
函子F(X:S):sig val F:'a X.t->'a a.t end=结构乐趣f(X.C)=X.C|f(X.D(X,y))=公元结束
我们保证X吨A.t公司相同类型和公元X、D是一样的数据构造函数。

如下所述第1.3.6节,数据类型复制规范的主要用途之一是替代定义共享约束,即不再允许。

1.3.3. 使用修改签名其中类型

有时,当我们需要时,使用定义类型规范“为时已晚”一个。通过这一点,我指的是我们想要的类型define在已声明的签名中指定,也许在图书馆。例如,假设S1(第一阶段)是一个大的具有简单类型规范的签名t吨:

签名S1=信号发生器t型…(*二十种其他规格*)结束
稍后我们要使用S1(第一阶段),但我们希望在中指定另外t吨int列表).

一个可以插入S1(第一阶段)的定义签名表达式代替S1(第一阶段),修改为适当的定义t吨,但当S1(第一阶段)是一个很大的签名。SML’97以其中类型定义。它可以修改通过添加其类型之一的定义来创建现有签名。在我们的示例中,我们将按如下方式使用它:

签名S2=信号发生器结构A:S1,其中类型t=int列表结束
给你其中类型定义修改或增加了签名S1(第一阶段)、和S1,其中类型A.t=int是一种新的签名表达式。使用其中类型我们能够表达以前无法表达的东西定义共享约束,如
签名S2=信号发生器结构A:S1,其中类型t=int列表结束
我们甚至可以使用其中类型条款,如果要定义的类型埋在子结构内,如以下示例
签名U=信号发生器结构A:S1...结束签名S2=信号发生器结构B:U,其中类型A.t=int list结束
请注意,这里是其中类型条款是符号路径A.t公司(a)长子在中定义的术语),其效果是增加规范t吨在里面S1(第一阶段).

由where子句定义的数据类型。其中类型子句可以是一个指定为数据类型的。

结构A=结构数据类型'a t=C|D of'a*'a t结束签名S=信号发生器数据类型'a t=C|D of'a*'a t结束签名U=S,其中类型“a t=”a a.t
在本例中其中类型相当于嵌入式数据类型复制规范,so签名U型能够等效定义为
签名U=信号发生器数据类型t=数据类型A.t结束
从技术上讲,该定义对受影响的主要规范及其在where中的定义条款。例如,以下签名声明是合法的,
签名S=信号发生器数据类型t=t结束其中类型t=int
虽然这样的签名无法实现,因为基本规范t吨及其在中的定义where子句不一致。编译器可能会检查这些明显的不一致(但SML/NJ没有)。

where定义右侧的上下文。右侧的类型表达式哪里类型定义不能提及指定的类型它在签名中修改。下面的例子是非法(假定没有类型t吨定义在本声明的上下文)。

签名S=信号发生器类型st型结束其中类型s=t
这种限制的原因是我们想避免示例如下所示
签名S=信号发生器类型s结构A:信号发生器t型值x:s*t结束结束签名U=S,其中类型S=A.t*A.t
如果A.t公司位于的右侧哪里类型子句引用签名中指定的类型S公司,那么我们将创建一种不受欢迎的循环依赖项,其中类型取决于底座A类因为where定义,而A类取决于因为在中提到A类的签名。

我们希望能够使用替换任何签名定义这个其中类型用等价定义构造将where定义替换为类型缩写规范和数据类型复制规范。很明显签名U型在最后一个示例中不能是在没有其中类型本解释中的条款该子句右侧的上下文。这就是为什么我们从上下文中排除已修改签名的正文右侧,从而使此示例非法。

SML/NJ差异:自然思维方式这个其中类型构建是一种引入类型缩写规范(或数据类型复制规范)转换为先前定义的签名,从而生成扩展的签名的版本。原则上应该可以用放置的定义等效地定义该增强签名直接在受影响类型的规范点。这个事实上,SML/NJ是如何实现的其中类型定义。因此
签名S=信号发生器t型结束其中类型t=int
由编译器转换为声明
签名S=信号发生器类型t=int结束
因此,将类型缩写规范视为主要结构定义其中类型就这一点而言。

然而,由于技术原因,定义做的事情是向后的,治疗其中类型作为一个基本概念和定义类型缩写规范(但不是数据类型复制规范)其中类型包括.因此,在定义中,

签名S=信号发生器类型t=int结束
被翻译成
签名S=信号发生器包括sig类型t end,其中类型t=int结束
在大多数情况下,这种方法上的差异并不重要。定义的版本允许更多签名合法,但我们辩称它们不是优秀程序员会有的签名想要写作。下面是一个合法签名的示例SML/NJ不接受(由于Marin Elsman的原因,请参阅SML/NJ错误1330):
签名S=信号发生器类型s结构U:sig键入'at类型u=(int*real)tend where类型'a t=s结束,其中类型U.U=int
我们把它留给勤奋的语言律师去验证根据定义,这是可以接受的,但这是非常清楚的这不是我们想鼓励程序员编写的那种东西。确定此签名是否有意义是建立高阶变量的方程组(,u个、和t吨)然后寻求该系统的解决方案。我们希望签名定义的敏感性应该更加明显。

此签名声明不被接受的一个特殊原因SML/NJ认为SML/NJ不允许其中类型要应用于已具有定义规范。签名的一个简单示例因此被拒绝的是:

签名S=信号发生器类型s类型t=s结束其中类型t=int
写这篇文章更明智的方法是
签名S=信号发生器类型s类型t=s结束其中类型s=int
哪一个SML/NJ可接受。

1.3.4. 共享到本地路径的限制

在SML’90中,共享规范是一个独立的规范在签名中,但在SML'97中,共享规范修改了它们在文本中遵循的规范(顺序规范)签名。共享约束中提到的组件必须所有这些都来自共享约束修改的规范。因此,在

[1] 签名S=[2] 信号发生器[3] 类型s[4] 结构A:sig[5] 类型t[6] u型[7] 共享类型t=u[8] 共享类型t=s[9] 结束[10] 结束
第[7]行中的共享约束适用于行[5]和[6],并且是合法的,因为类型名称t吨u个共享约束中提到的这些规格。另一方面,第[8]行的共享约束,不合法,因为其“范围”由行组成[5-7],而提到类型绑定lin行[3]。同样的规则适用于结构共享约束。

共享约束的这种范围限制是主要原因为什么共享约束可能需要转换为定义约束规范或哪里定义。声明S公司以上内容可以改写为以下97年的合法SML宣言。

[1] 签名S=[2] 信号发生器[3] 类型s[4] 结构A:sig[5] 类型t=s[6] 类型u=t[7] 结束[8] 结束
它也可以合法重写(根据定义)为
[1] 签名S=[2] 信号发生器[3] 类型s[4] 结构A:sig[5] 类型t=s[6] u型[7] 共享类型t=u[8] 结束[9] 结束
但这个版本提出了一个微妙而有争议的观点。通知类型t吨在其规范中定义为第[5]行,并且在第[7]行。

1.3.5. 共享和定义规范不受干扰

在SML’90中,有两种类型共享,一种是所涉及的类型是“正式的”或未知的,我们称之为一致性共享和另一种类型,其中一种类型未知,另一种未知已知,我们称之为定义共享。这是一个典型的相干共享示例:

签名S=信号发生器t型类型s共享类型t=s结束
在这种情况下,两者都不是t吨也不是确定,但共享规范要求在实例化它们时都实例化为同一类型。

定义共享的一个简单示例是:

签名S=信号发生器t型共享类型t=int结束
其中类型t吨有效定义为等于整数共享约束。

在SML'97中,可以在其原始规范,或事后使用“where-type”语法,所以定义共享似乎是多余的。此外,相互作用在定义共享和直接定义之间可以创建谜题,例如:

签名S=信号发生器类型s类型t=s列表键入'a'u类型v=int u共享类型t=v结束
这里,类型t吨v(v)直接在中定义形式类型的术语u个,然后共享规范将它们等同起来。如果我们想的话确定此签名是否可满足,我们必须查看是否存在满足方程的类型s和u
s列表=int u
求解这样的二阶方程通常是一个棘手的问题,也许无法解决。为了解决这个问题,SML’97设计通过禁止定义共享来排除此类示例单词,共享规范中涉及的类型需要要正式或“灵活”。

从技术上讲,定义要求涉及类型构造函数在共享约束中,应(1)未定义为类型函数,以及(2)未根据某些“刚性”类型构造函数(即类型先前在上下文中定义的构造函数)。

我们选择将“可共享”定义为:应用于类型构造函数的定义。我们将使用该术语“defined”与sharable相反。更微妙的定义是可能;参见注释“共享类型”如下所示。

因此,以下签名是非法的

签名S=信号发生器类型s=int(定义了*s)类型t(*t可共享*)共享类型t=s(*s不可共享*)结束
并且必须重新表述为(例如):
签名S=信号发生器类型s=int类型t=s结束
使用“where-type”定义,事情会稍微复杂一些。内部类型共享约束可能受外部定义的影响约束,如下例所示:
签名S1=信号发生器类型st型共享类型t=s(*好,因为s和t在这里是可共享的*)结束其中类型t=int;(*这将s和t转换为刚性类型*)
这是合法的,但以下声明不是:
签名S2=信号发生器结构A:S1v型共享类型v=A.s(*A.s不灵活*)结束;
然而,S2可以很容易地转换为下面的合法S3用定义替换外部共享约束。
签名S3=信号结构A:S1类型v=A.s结束;
通常,我们建议避免共享可以轻松实现的约束用定义规范表示。所以你应该总是喜欢
类型t=s
t型共享类型t=s。
这同样适用于结构共享和结构定义规范(它们是SML/NJ语言的扩展)。违反这一新规定强制约束通常可以通过替换结构来消除通过结构定义规范共享,例如替换
结构A:SIGA共享A=B.C
具有
结构A:SIGA=不列颠哥伦比亚。

1.3.5.1具有相同签名的结构共享的SML/NJ例外

SML/NJ为共享刚性规则提供了一个重要例外类型。这种情况下,类型共享由结构隐含在具有相同签名的两个结构之间共享。

下面是一个示例

签名S=信号发生器类型t=int结束签名S1=信号发生器结构A:S结构B:S共享A=B结束
这在SML/NJ中是允许的,因为A和B具有相同的签名,甚至尽管共享约束等价于
共享类型A.t=B.t
A.t和B.t有严格的规范
类型t=int。

1.3.5.2哪些类型是可共享的(主要针对语言律师)

关于构造函数应该是什么类型存在一些争议允许共享约束。我们可以通过以下示例

签名S=信号发生器类型s类型t=su型共享类型t=u结束
根据我们的上述定义,t吨已定义,因此不可共享,以及此签名声明被拒绝。然而,从技术上讲的语义表示t吨签名中是类型函数\().(()纳秒)(nullary型函数,其中纳秒是语义类型的“名称”),并且此类型函数是eta-等价的纳秒,一个简单的灵活类型名称。因此,如果这个eta还原是假设,t吨符合定义的要求,可以出现在共享约束。另一方面,考虑
签名S=信号发生器类型s类型t=s列表u型共享类型t=u结束
以下是t吨是类型函数\().(()ns)列表,它不会简化为简单的类型名,因此共享约束显然是非法的。我们采用更简单、更具限制性的可分享的是,它更容易解释,而且它接纳了所有合理的因素用法。我们声称,它在签名书写。它还可以更简单、更高效地实施(至少对于SML/NJ)。以下是根据更复杂的定义版本(感谢DIKU):
签名S1=信号发生器t型类型s=tend,其中类型s=int签名S2=信号发生器t型类型s=t共享类型t=s结束签名S3=信号发生器类型s结构U:信号发生器键入'at类型u=(int*real)tend where类型'a t=s结束,其中类型U.U=int
尚不清楚这样的例子对任何人都有任何重要性除了语言律师。最后一个特别反常:阅读签名不应该是解谜的练习!

1.3.6. 用定义规范替换“定义”共享

我们过去可以使用定义共享规范,如图所示:

签名S2=信号发生器结构A:S1共享类型A.t=int结束
但这种分享并不合法,因为整数不是主体的局部路径S2系列.在某些情况下,您可以选择使用共享规范或类型定义规范或其中类型条款。例如,以下三项签名声明S公司等效:
签名T=签名类型结束签名S=信号发生器t型结构A:T共享类型t=A.s结束签名S=信号发生器t型结构A:sig类型s=t端(*扩展t*)结束签名S=信号发生器t型结构A:T,其中类型s=T结束
最后一个示例可能表明,可以随时替换类型共享规范与类型定义规范或哪里类型子句,但情况并非如此,如图所示通过以下示例:
签名S=信号发生器类型s结构A:sig数据类型d=第d个,共s个数据类型t=K结束共享类型s=A.t结束
无法重新排列签名的定义S公司以便共享规范可以替换为定义。然而,这个例子有点做作尚不清楚是否会产生任何严重的实际影响如果我们放弃写这种签名的能力使用定义规范代替共享。

这个其中类型construct还可用于定义类型在基签名中指定为数据类型,这使得它与下面描述的数据类型复制规范有关。这里有一个例子。

结构A=结构数据类型t=t结束签名S=信号发生器数据类型t=t结束签名S1=S,其中类型t=A.t
这种能力可以定义中需要数据类型规范以取代定义共享约束,如以下示例:
签名S=信号发生器数据类型t=t共享类型t=A.t结束
本例中的定义不需要检查规范的数据类型“签名”及其在其中类型子句必须一致,但编译器可以执行某种级别的检查。至少可以检查一下定义右侧的类型也是数据类型,除此之外,它可能会检查数字和数据构造函数的名称一致。

1.3.7. 结构共享作为类型共享的派生形式

在SML’97中,结构共享从一流变弱派生语法特征,即解释的特征在转换为类型共享语法方面。这是真的模块系统的表现力减弱。在SML’90中,其中每个结构都具有唯一的静态身份和结构可以静态检查共享,可以使用结构共享不仅要保证两个结构中的类型一致,而且要保证构成这些类型关联接口的值还包括共享。例如,如果两个结构A类B类有签名作战需求文件定义如下

签名ORD=信号发生器t型值比较:t*t->t结束
共享规范
共享A=B
不仅保证了类型A.t公司B.t公司是相同的,但比较操作A.comp公司B.comp公司都是一样的,即A类B类同一类型。

在SML'97中,我们仍然可以编写这样的共享规范,但它们被视为所有可能的隐含类型共享的缩写规范。在这种情况下,上述结构共享规范转换为以下类型共享规范:

共享类型A.t=B.t
这并不意味着比较运算符A.comp和B.comp同意,因此它严格弱于SML中的结构共享规范'90.

可以恢复旧的结构共享功能当所讨论的结构包含(生成)类型时用于唯一标识结构。实际上,使用了类型作为一个独特的标签来保证整个结构的身份。如果没有“作弊”,也就是说,如果没有其他独立的作弊者,这种做法是可行的定义的结构包含相同的标记类型。

关于结构共享的弱版本需要注意的另一件事它是不可传递的。例如

共享A=B和B=C
并不一定意味着
共享A=C
感到满意。下面是一个示例
签名S0=信号发生器t型结束签名S=信号发生器结构A:S0结构B:信号端结构C:S0共享A=B和B=C结束
自结构B类没有类型组件,共享方程A=BB=C是空虚的,并且转化为完全没有类型共享。特别是,他们不保证那个A.t=C.t,因此并不意味着共享约束A=C.

出现的路径是结构共享规范,也必须满足中描述的位置限制章节1.3.4。也就是说,指定共享的结构必须都是具有相同签名的组件(或子组件)包含共享规范。这将确保类型共享结构共享转换为的规范将满足类型共享的局部性约束。

这种地方性规则通常会排除“定义结构”共享规范”,这在SML'90中使用得相当频繁。怎么我们应该重写这样的定义结构共享规范吗?一个方法是将结构共享扩展到相应的集合类型共享规范,然后替换定义的类型共享规范中包含适当的“where-type”定义。例如,给定结构a,定义如下:

结构A=结构数据类型t=t结束
SML’90签名
签名S=信号发生器结构B:sig型t端共享B=A结束
可以重写为
签名S=信号发生器结构B:sig类型t端,其中类型t=A.t结束
当然,如果a中涉及十几种类型,而不仅仅是第一,这可能导致代码不愉快的扩展。为了这个原因是,最好有一个结构类似物定义规范,以便我们可以编写如下内容:
签名S=信号发生器结构B:sig类型t端=A结束
事实上,SML/NJ支持这种定义结构规范;看见第1.6.2节.

1.3.8.包括六倍x射线光电子能谱

的语法包括规范已推广以允许任意签名表达式以及签名名称。此扩展的目的是允许定义类型规范定义为包括哪里类型作为派生形式。因此

类型t=第y天
应该是的缩写
包括sig类型t结束,其中类型t=第y天
这似乎有点复杂,因为简单类型定义规范就其本身而言,似乎是一个更基本的概念,但这之所以选择这种方法,是因为定义。简单处理存在技术问题定义规范作为基本构造和定义哪里类型通过句法翻译为定义规范,以及这就是自由变量捕获的问题。例如,在遵循签名定义u个出现在中其中类型子句与类型不同u个在正文中指定S公司.
签名S=信号发生器u型t型结束其中类型t=u*u
如果我们要消除其中类型中的子句通过文本将定义向内移动到t吨是指定的,我们会签名S=信号发生器u型类型t=u*u结束这显然是不正确的,因为u个在定义中t吨已被当地人捕获装订u个因此,尽管这种消除其中类型可以在语义结构层面工作,它不作为文本转换工作(即作为派生形式)。

1.3.9. 不透明签名匹配:>

以前版本的SML/NJ支持特殊结构声明形式

抽象S:SIG=结构。。。结束
其目的是创建签名的“抽象”实例信号。SML'90中没有此功能原因,但它的需要是真实的。

在SML'97中,提供了类似的功能使用特殊签名约束的“不透明”签名匹配语法S:>信号。例如,以下声明与上面给出的旧“抽象”声明具有相同的效果。

结构S:>SIG=结构。。。结束
不透明签名匹配的效果是正式的或灵活的签名SIG中的类型获得新的抽象实例化,这些实例化是与结构表达式中的相应类型不同受到约束。下面是一个更详细的示例:
签名SIG=信号发生器类型s类型t=intval零:sval成功:s->s值f:s->t结束结构运输:SIG=结构类型s=int类型t=int值0=0乐趣成功x=x+1乐趣f x=x结束结构不透明:>SIG=结构类型s=int类型t=int值0=0乐趣成功x=x+1乐趣f x=x结束
由于正常的透明签名匹配,Transp.s是相当于整数,运输也是如此。但在不透明,不透明。t吨仍然相当于整数,因为签名中的定义,但不透明。是不同于整数.

在两个地方使用不透明签名可能有意义匹配。在上述正常结构声明中的第一个示例,然后指定函子的结果签名。

函数F(X:PSIG):>SIG=结构。。。结束
当函子定义为具有如下不透明结果签名时,每次应用函子时结果签名已创建。

使用不透明的签名匹配形式是没有意义的函子的形式参数(即在输入端作为与输出端相反)。

1.3.10. 相等类型规范,推断相等属性

1.3.11.打开地方的规范表格已删除

SML’90的定义有两种特殊形式的规范可用于签名:本地。。。在..端打开这些表格,特别是当地规范,包括由于定义中的技术原因引入。这些规范表格都已在SML'97中删除。这些SML’90的功能存在争议,因为当完全使用时一般来说,他们的行为没有明确的直觉依据,尽管它们具有精确的技术含义和技术作用在定义的语义中。除了一个特殊功能外,这些功能的实用性也值得怀疑限制案例,开放和本地相结合,允许规范中类型路径的缩写。例如:

结构A=结构类型t=int结束;签名S=信号发生器本地打开A in值x:t结束结束;
给你打开A规范允许A.t公司缩写为t吨,而地方的形式阻止组件A类从被添加到S公司按照规范。所以这个签名的定义S公司等于写作
签名S=信号发生器值x:A.t结束;

在SML/NJ,根据SML’90定义,但部分实现了只支持上面的路径缩写习惯用法。事实上,公开表单的实现使其可以用于缩写即使没有周围的本地规范也会产生影响。因此,在SML/NJ 0.93中写得简单是可能的

签名S=信号发生器打开A值x:t结束;
具有相同的效果。

由于这些表单在SML'97中已删除,因此在转换时SML’90(或SML/NJ 0.93)代码,有必要发现并消除使用本地和开放式签名,并写出完整类型的路径对于那些因此被缩写的类型。

在其全部通用性中使用本地规范的任何(非SML/NJ)代码要达到某种奇怪的效果,必须重写才能实现以其他方式达到预期效果[数据库管理:我会的有兴趣看到这方面的例子]。

1.3.12. 标识符没有重复的规范

1.3.13. 的行为包括(Defn vs SML/NJ)


戴夫·麦奎因
上次修改时间:2001年6月28日星期四17:23:44 EDT