模块系统

目录

基础

首先让我们介绍一些术语。A类定义是一个句法定义实体(如函数或数据类型)的构造。A类名称是用于标识定义的字符串。相同的定义可以有很多名称,在程序的不同点,它将有不同的名称。也可能是两个定义同名的情况。在这种情况下如果使用该名称,将是一个错误。

模块系统的主要目的是构造名称在一个程序。这是通过在以下层次结构中组织程序来实现的模块,其中每个模块包含许多定义和子模块。对于例如,

模块Main,其中data Nat:设置位置零:自然成功:国家->国家模块B,其中f:自然->自然f n=吸入克:自然->自然->自然g n m=B.f m

注意,我们使用缩进来指示哪些定义是模块的一部分。在这个例子中如果在模块中主要。B类在中主要.如何引用特定定义取决于其所在位置模块层次结构。封闭模块的定义由以下内容引用他们的名字如果以上。从访问定义在其定义模块外,必须使用限定名,如引用B.f公司在定义中以上。

为了能够在模块中使用定义的短名称,模块必须已打开:

模块Main,其中data Nat:设置位置零:自然成功:国家->国家模块B,其中f:自然->自然f n=吸入打开Bg:天然气->天然气->天然气g n m=f m

如果A.q名称指的是一个定义d日然后在之后打开A,q名称还将参考d日。请注意q名称本身可以是限定名。打开模块只为定义引入新名称,从不删除旧名称。这个策略是允许引入不明确的名称,但如果使用了不明确的名称。

私人定义

要使定义在其定义模块外不可访问,可以声明该定义私有的。私有定义被视为定义它的模块,但在模块外部,定义没有名称。在一个独立类型设置私有定义存在一些问题,因为类型检查器执行计算,私有名称可能会显示在目标和错误消息。考虑以下(人为的)示例

模块Main,其中模块A,其中private IsZero’:自然->设置IsZero’Zero=真IsZero’(成功)=假IsZero:自然->设置IsZero n=IsZero’n打开Aprf:(n:Nat)->IsZero nprf n=?0

}

目标的类型?0IsZero n(零)正常化为IsZero'n(零). The问题是如何向用户显示这种正常的表单。?0没有名字IsZero(零)一种选择是尝试折叠术语并打印IsZero语言一般来说,这是一个非常困难的问题,因此为了做到这一点,我们向用户明确表示IsZero(零)是什么东西不在范围内并将目标打印为.Main.A.IsZero'n.领先者点表示实体不在范围内。同样的技术用于只有模糊名称的定义。

实际上,使用私有定义意味着从用户的角度来看,我们这样做没有主题缩减。然而,这只是一个幻觉——类型检查器具有对所有定义的完全访问权限。

名称修改器

将定义私有化的另一种选择是对什么进行更精细的控制打开模块时会引入名称。这是通过限定一个开口来完成的带有一个或多个修饰符的语句使用,躲藏,或重命名.你可以将两者结合使用躲藏具有重命名但不是彼此。这个的影响

使用(x1;…;xn)重命名(y1到z1;…;yn到zn)打开A

就是介绍名字X轴z秒哪里x个指相同的定义为A.x公司z(z)A.年注意,如果X轴重叠将为同一定义引入两个名称。我们不允许X轴z秒重叠。定义了其他形式的开口就这个而言。A类表示中的所有(公共)名称A类.然后

打开A重命名(从ys到zs)==打开隐藏()重命名(ys到zs)打开隐藏(xs)重命名(ys到zs)==使用(A-xs-ys)重命名(ys到zs)打开A

省略的重命名修饰符相当于空重命名。

重新导出名称

一个有用的功能是能够从另一个模块重新导出名称。对于例如,可能需要创建一个模块来从中收集定义其他几个模块。这是通过用限定开放语句来实现的这个公众的关键词:

模块Nat,其中Nat:设置模块Bool,其中Bool:设置模块前奏曲,其中打开Nat public打开Bool public零:自然->布尔

这将引入定义前奏曲。国家前奏曲。布尔它们是从外部可见前奏曲模块。

参数化模块

到目前为止,所讨论的模块系统功能仅涉及范围操纵。现在,我们将注意力转向一些更高级的功能。

有时能够在给定的签名中临时工作是有用的。对于例如,在定义用于排序列表的函数时,可以方便地假定列表元素集A类和订单结束A类.合同中这可以通过两种方式完成:使用函子,本质上是模块之间的函数,或使用部分。部分允许您执行以下操作同时从几个定义中抽象出一些论点。我们介绍参数化模块类似于Coq中的部分。在声明模块时可以给出从所有模块中的定义。例如,排序函数的简单实现如下所示:

模块排序(A:Set)(_≤_:A->A->Bool),其中插入:A->列表A->列表A插入xε=x::ε插入x(y::ys),其中x≤y插入x(y::ys)| true=x::y::ys插入x(y::ys)|false=y::插入x y排序:列表A->列表A排序ε=εsort(x::xs)=插入x(排序xs)

如前所述,对模块进行参数化可以抽象参数覆盖模块中的定义,因此在排序模块we

插入排序:(A:设置)(_≤_:A->A->Bool)->A->列表A->列表A排序:(A:集合)(_≤_:A->A->Bool)->列表A->列表A

对于函数定义,显式模块参数变为显式参数抽象函数的参数和隐式参数变为隐式参数。然而,对于构造函数,参数总是隐式参数。这是由于模块参数转换为数据类型参数,并且数据类型参数是隐式的构造函数的参数。这也恰好是合理的做法。

在Coq中不能做的事情是对其参数应用节。我们允许通过模块应用程序语句执行此操作。在我们的示例中:

模块SortNat=Sort Nat leqNat

这将定义一个新模块SortNat(排序国家)如下

SortNat模块,其中插入:Nat->List Nat->列表Nat插入=Sort.insert Nat leqNat排序:列出国家->列出国家sort=排序。排序Nat leqNat

新模块也可以参数化,您可以使用名称修饰符控制应用原始模块中的哪些定义和名称他们已经在新模块中了。模块应用程序的一般形式是

模块M1Δ=M2术语修饰符

一种常见的模式是将模块应用于其参数,然后打开结果模块。为了简化这一点,我们引入了速记

开放模块M1Δ=M2术语[公共]mods

对于

模块M1ΔM2术语mods打开M1[公众]

在多个文件上拆分程序

在构建大型程序时,能够将程序分割开来是至关重要的多个文件,不必对每个文件进行类型检查和编译更改。模块系统提供了一种结构化的方法来实现这一点。我们将程序定义为模块的集合,每个模块都在单独的文件。要访问不同文件中定义的模块,您可以进口模块:

导入M

为了实现这一点,我们必须能够找到模块所在的文件定义。为此,我们需要顶级模块A.B.C公司定义于文件卡格达在目录中A/B(A/B)/人们可以想象为import语句指定一个文件名,但这意味着会将程序中包含有关文件系统的详细信息,这不是很好。

导入模块时M(M)模块及其内容作为如果模块已在当前文件中定义。为了访问必须打开的模块内容的非限定名称。类似于模块应用程序我们介绍了速记

打开导入M

对于

导入M打开M

有时导入模块的名称与本地模块冲突。在这个如果可以用不同的名称导入模块。

将M导入为M'

也可以将修饰符附加到import语句,限制或更改模块内部可见的名称。

页面上次修改时间:2013年5月3日11:00 pm
技术支持私人维基