使用相关类型编程:向量
在下面的代码中,我们对向量(在计算机的意义上科学,而不是数学意义上的)。大致来说,向量是具有确定长度的对象列表。
模块 你好-世界环保组织 哪里
打开 进口 数据。国家 使用 (ℕ; 零; 成功)
数据 Vec公司 (一个 : 设置) : ℕ → 设置 哪里
[] : Vec公司 一个 零 _Ş_ : ∀ {n个} (x个 : 一个) (X轴 : Vec公司 一个 n个) → Vec公司 一个 (成功 n个)
中缀 5 _∷_
将上述代码粘贴或键入到名为的新文件中你好,世界新闻部
.加载文件(在Emacs中抄送 C-l公司
). 这个还保存文件。如果正确加载了agda源代码应该看到代码被突出显示,并看到一条消息*全部已完成*.
注释
如果文件没有类型检查,Agda将投诉。通常光标将跳转到错误的位置,错误将(默认情况下)加下划线。有些错误会得到处理不过,情况有所不同。如果Agda看不到定义终止/生产将在轻鲑鱼、和如果目标以外的元变量无法解决,则代码将在中突出显示黄色的(突出显示可能不会出现直到重新加载文件后)。对于后一种情况您仍然可以使用该文件,但Agda将(通过默认)拒绝将其导入另一个模块,如果函数未终止Agda可能挂起。请参见背景突出显示查看Agda使用的不同背景颜色的完整列表。
提示
如果您不喜欢Agda语法或错误的方式突出显示(例如,如果你是彩色盲),那么你可以通过键入来调整设置M-x公司 自定义组 房地产税
agda2高亮显示 房地产税
在Emacs中(加载Agda文件后)和按照说明操作。
Agda项目的结构包括模块每个Agda文件有一个顶层模块其名称必须与文件名匹配,并且零个或多个嵌套模块。每个模块都包含一个列表声明。此示例有一个名为你好,世界各地
,它有三个声明:
安打开 进口
导入数据类型的语句ℕ
及其施工人员零
和成功
来自模块数据。国家
并将其纳入范围,
一个数据
定义数据类型的声明Vec公司
具有两个构造函数:空向量构造函数[]
和这个欺骗建造师_∷_
,
最后是一个中缀
声明指定优先对于欺骗操作。
提示
Agda使用Unicode码源文件中的字符(更具体地说:UTF-8型字符编码),例如ℕ
,→
、和∷
在本例中。可以使用相应的乳胶命令名。收件人学习如何输入unicode字符,将光标移到它上面并进入M-x公司 describe-char(描述)
或C-u(C-u) C-x公司 =
。这将显示所有有关字符的信息,包括如何使用Agda输入法。例如,输入ℕ
你可以键入\Bbb{N}
或\bN(英国)
。请参阅Unicode输入对于有关输入unicode字符的详细信息。
数据类型Vec公司
让我们从定义的第一行开始Vec公司
:
数据 Vec公司 (一个 : 设置) : ℕ → 设置 哪里
此行声明了一个新的数据类型并为其命名Vec公司
.单词数据
和哪里
是关键字,而部分Vec公司 (A) : 设置) : ℕ → 设置
决定的类型Vec公司
.
Vec公司
不是单一类型,而是类型系列.这个家庭类型有一个参数 一个
类型为设置
(这是分类属于小型,例如ℕ
,布尔
,…)和一个指数类型为ℕ
(类型自然数)。参数一个
表示的对象类型向量。同时,索引表示向量的长度,即它包含的对象数。
总之,这一行告诉我们,对于任何具体类型B类 : 设置
和任何自然数米 : ℕ
,我们正在宣布一个新的类型Vec公司 B类 米
,它也属于设置
.
施工人员[]
和_Ş_
数据类型的每个构造函数都在单独的一行上声明,并且缩进一个严格为正数的空格(在本例中为两个)。
我们选择了这个名字[]
对于第一个构造函数。它表示空向量,其类型为Vec公司 一个 0
,即它是一个长度向量0
.
第二个构造函数是mixfix运算符命名_∷_
(发音欺骗). 对于任何数字n个 : ℕ
,它将对象作为输入一个
和长度向量n个
.作为输出,它生成一个长度为成功 n个
,的继任者n个
.数字n个
它本身是一个隐式论证给施工方_∷_
.
带有关键字的最终声明?nfixr公司
不属于数据类型声明本身;因此它没有缩进。它建立优先操作员的_∷_
.
提示
您可以让Agda使用“Deduce”推断表达式的类型type'命令(抄送 C-d公司
). 第一次按下抄送 C-d公司
要打开提示,请输入例如,术语3 ∷ 2 Ş 1 Ş []
,然后按回车键。阿格达推断键入并返回类型Vec公司 ℕ 3
,表示给定术语为包含3个类型对象的向量ℕ
.
注释
标识符中几乎可以使用任何字符(如α
,∧
,或♠
例如)。因此大多数词汇单位之间必须有空格。例如3∷2∷1∷[]
是有效的标识符,因此我们需要写入3 ∷ 2 ∷ 1
∷ []
而是让Agda成功解析它。
总功能查找
既然Vec公司
定义后,我们继续定义查找
功能给定向量和位置,返回给定位置的向量。相比之下查找
功能我们可以用大多数(非依赖类型)编程语言定义,该函数的这个版本是全部的:所有调用都是保证在有限时间内返回值,不可能错误。
为了定义此函数,我们使用翅片
来自标准的数据类型库。翅片 n个
是具有的类型n个
对象:数字0
到n-1个
(以一元符号表示零
,成功 零
,…),我们用来建模n个
长度向量中的可能位置n个
.
现在创建一个名为您好,全球dep-lookup.agda
文件并键入或粘贴:
模块 你好-世界dep查找 哪里
打开 进口 数据。国家 使用 (ℕ)
打开 进口 数据。Vec公司 使用 (Vec; _∷_)
打开 进口 数据。翅片 使用 (翅片; 零; 成功)
变量
一个 : 设置
n个 : ℕ查找 : Vec公司 一个 n个 → 翅片 n个 → 一个查找 (一 ∷ 作为) 零 = 一查找 (一 ∷ 作为) (成功 我) = 查找 作为 我
这个Vec公司
我们之前看到的类型实际上已经在模块中数据。Vec公司
标准库,因此我们导入它而不是复制前面的定义。
我们已经宣布一个
和n个
作为可推广变量避免宣布隐式参数。这允许我们使用一个
和n个
在类型中属于查找
没有显式绑定名称。更明确地说,完整类型的查找
(我们可以通过使用抄送 C-d公司
)是:
查找 : {一个 : 设置} {n个 : ℕ} → Vec公司 一个 n个 → 翅片 n个 → 一个
警告
零
和成功
是不的建设者ℕ
我们之前见过,但更确切地说是翅片
.Agda允许过载构造函数名称,并根据预期类型消除它们之间的歧义使用它们的位置。
定义查找
函数指定了两种情况:
没有空向量的情况[]
.这不是错误:Agda可以根据查找
就是这样无法在空向量中查找对象,因为存在没有类型的可能索引翅片 0
。有关更多详细信息,请参阅一节在覆盖范围检查.
Agda作为证明助手:证明加法的关联性
在本节中,我们陈述并证明了自然加法的结合性Agda中的数字。与上一节相比,我们通过以下方式构建代码行行。要在Emacs中遵循此示例,请重新加载文件按添加每个步骤后抄送 C-l公司
.
关联性声明
我们首先创建一个名为您好,世界一流
.粘贴或键入以下代码:
现在我们导入数据类型ℕ
和加法运算_+_
,均在Agda Builtin库中定义。
接下来,我们导入命题等式类型 _≡_
来自模块关系。二元的。命题等式
.
打开 进口 关系。二元的。命题等式 使用 (_≡_)
在咖喱-霍华德信件,类型x个 ≡ 年
与以下命题相对应:x个
和年
是相等的物体。通过编写返回类型对象的函数x个 ≡ 年
,我们是证明这两项相等。
现在我们可以说明关联性:给定三个(可能不同的)自然数字,将第一个数字与第二个和第三个数字相加计算出与第一个和第二个的相加值相同的值第三名。我们将此声明命名为+-协会会员
.
+-协会会员 : 设置+-协会会员 = ∀ (x个 年 z : ℕ) → x个 + (年 + z) ≡ (x个 + 年) + z
这还不是证据,我们只是写下了声明(或发音)具有结合性。
结合性证明
声明+-协会会员
是的成员设置
,即它是一个类型。现在我们已经用Agda的方式陈述了财产理解,我们的目标是证明它。要做到这一点,我们必须构造类型为的函数+-协会会员
.
首先,我们需要导入构造函数零
和成功
的已导入的数据类型ℕ
和构造函数回流
(以下简称自反性)和功能刚果
(以下简称同余)来自标准库.
打开 进口 数据。国家 使用 (零; 成功)
打开 进口 关系。二元的。命题等式 使用 (回流; 刚果)
为了证明+-协会会员
我们需要找到一个这样的物体类型。在这里,我们将此对象命名为+-防联想的
.
+-防联想的 : ∀ (x个 年 z : ℕ) → x个 + (年 + z) ≡ (x个 + 年) + z
如果我们现在加载文件,Agda会给出一个错误:“以下名称是声明但未附带定义:+-防联想的
”. 事实上,我们只有声明了的类型+-防联想的
但尚未给出定义。要构建定义中,我们需要了解更多有关孔和壳体拆分的信息。
开孔和分壳
我们可以让Agda使用其交互模式帮助我们编写证明。首先,我们首先写一个简单的子句,这样即使我们仍然可以加载文件不知道证据。该子句由属性名称、输入变量,等于符号=
和问号?
.
当我们重新加载文件时,Agda不再抛出错误,而是显示消息*所有目标*有一个目标列表。我们现在已经进入互动验证模式。阿格达把我们的问号变成了所谓的孔 { }0个
带有标签0
。每个孔都是程序的一部分的占位符这仍然是不完整的,可以通过交互方式进行优化或解决。
注释
你不应该进入这样的洞{ }0
手动,加载文件时,Agda负责编号。要插入孔,写入其中之一?
或{! !}
并加载文件以使Agda分配它的唯一编号。
要获取有关特定的孔,将光标放在其中并按抄送 C-,
。此显示孔的类型,以及范围中所有变量的类型。在这个例子中,我们得到目标类型是x个 + (年) + z) ≡ x个 + 年 + z
,有三个变量x个
,年
,和z
在范围内,所有类型ℕ
.
注释
你可能想知道为什么Agda会显示这个词(x) + 年) + z
作为x个 +
年 + z
(不带括号)。这是因为中缀语句中缀 6 _+_
在进口商品中申报的阿格达。内置。国家
模块。此声明意味着_+_
手术是左联的。更多有关的信息mixfix运算符喜欢算术操作。您还可以检查这个关联性示例.
为了继续编写我们的证明,我们现在选择一个变量并执行一个案例拆分。要执行此操作,请将光标放在孔内,然后按抄送 抄送
.Agda要求输入要使用的模式变量的名称写x个
然后按回车键。这将前面的子句替换为两个新子句,其中一个x个
已被替换为零
还有另一个已被替换为成功 x个
:
+-防联想的 零 年 z = { }0+-防联想的 (成功 x个) 年 z = { }1
重要
这个x个
在的类型签名中+-防联想的
是不与x个
最后一个子句中的pattern变量,其中成功 x个
已写入。这个以下内容也适用:+-防联想的 (例如 x个₁) 年 z = { }1
.签名中声明的变量的范围限制为签名本身。
我们现在有两个洞,而不是一个洞。第一个孔具有类型年 + z ≡ 年 + z
,这很容易解决。为此,将光标放在标记的第一个孔内0
然后按下抄送 C-r公司
.这个用术语替换孔回流
,代表自反性和可以在任何时候使用w个 ≡ w个
对一些人来说学期w个
.
+-防联想的 零 年 z = 回流+-防联想的 (成功 x个) 年 z = { }1
现在我们还有一个漏洞需要解决。将光标放进去,然后按C-C公司 C-,
同样,我们得到了孔的类型:成功 x个 + (年) + z) ≡ 成功 x个 + 年 +
z
.Agda已经应用了_+_
更换左手边(例如 x个 + 年) + z
方程式的成功 (x) + 年 + z)
,并同样更换了右侧成功 x个 + (年) + z)
通过成功 (x) + (年)
+ z) )
.
提示
您可以使用转到定义
命令,方法是选择您要检查的定义,例如。_+_
并按下M-。
在Emacs中或C-M公司-\
在Atom中。这将带您了解_+_
,这是最初在内置模块中定义阿格达。内置。国家
.
提示
你可以让阿格达计算一个项的正规形式。为此,将光标放在剩余的孔中(孔中不应包含任何文本这一点)并按下抄送 C-n公司
。这将提示您输入一个表达式规范化。例如,如果我们输入(例如 x个 + 年) + z
我们回来了成功 (x) + 年 + z)
因此。
归纳法证明
如果我们现在看看剩下的洞的类型,我们会发现左侧和右侧从应用建造师成功
在这种情况下,足以证明两个参数成功
都是平等的。这一原则被称为同余属于平等_≡_
,用Agda函数表示刚果
.
要使用刚果
在这种情况下,我们需要将其应用于函数或构造函数成功
如果我们让阿格达推断刚果 成功
通过按压抄送
C-d公司
然后输入术语,我们就得到了类型{x 年 : ℕ} → x个 ≡ 年 →
成功 x个 ≡ 成功 年
换句话说,刚果 成功
将一个之间的平等x个
和年
并产生了一个新的平等证明成功 x个
和成功 年
.我们写作刚果 成功
在洞里再按一次抄送 C-r公司
以细化孔。这就产生了新线
+-防联想的 (成功 x个) 年 z = 刚果 成功 { }2
其中编号为2的新孔为类型x个 + (年) + z) ≡ x个 + 年 + z
.
为了完成证明,我们现在进行递归调用+-防联想的 x个 年 z
.注意这个有类型x个 + (年) + z) ≡ (x) + 年) + z
,这正是我们所需要的。为了完成证明,我们键入+-防联想的 x个 年 z
并用抄送 位形空间
.这将用给定的项替换孔,并完成证明。
注释
当我们定义这样的递归函数时,Agda执行终止检查这对于确保递归是有根据的,因此不会导致无效(循环)证明。在这种情况下,第一个参数x个
在结构上小于第一个论点成功 x个
在条款的左侧,因此是Agda允许我们进行递归调用。因为终止是不可判定属性,Agda将不接受所有终止函数,但仅接受那些被机械证明是终止的。
最后的证明+-防联想的
定义如下:
+-防联想的 零 年 z = 回流+-防联想的 (成功 x个) 年 z = 刚果 成功 (+-防联想的 x个 年 z)
当我们重新加载文件时,我们看到*全部完成*。这意味着+-防联想的
确实证明了这一说法+-协会会员
.
下面是“Hello world”证明示例的最后代码,包含所有导入在文件顶部:
模块 你好-举世无双的 哪里
打开 进口 数据。国家 使用 (ℕ; 零; suc; _+_)
打开 进口 关系。二元的。命题等式 使用 (_≡_; 回流; 刚果)
+-协会会员 : 设置+-协会会员 = ∀ (x个 年 z : ℕ) → x个 + (年 + z) ≡ (x个 + 年) + z+-防联想的 : ∀ (x个 年 z : ℕ) → x个 + (年 + z) ≡ (x个 + 年) + z+-防联想的 零 年 z = 回流+-防联想的 (成功 x个) 年 z = 刚果 成功 (+-防联想的 x个 年 z)
构建可执行的Agda程序
Agda是一种依赖类型的函数式编程语言。这意味着我们可以用Agda编写与世界互动的程序。在本节中,我们用Agda编写一个小的“Hello world”程序,编译并执行它。与上的独立示例相反Hello World页面,这里我们使用标准库编写一个同一程序的更短版本。
Agda源代码
首先,我们创建一个名为hello world项目
使用Emacs或Atom在我们称之为顶级文件夹的文件夹中。
{-#选项--防护#-}
模块 你好-世界进步 哪里
打开 进口 IO(输入输出)
主要的 : 主要主要的 = 运行 (输入StrLn “你好,世界!”)
快速逐行解释:
第一行是杂注(特别评论)它在文件顶部指定了一些选项。
第二行声明顶级模块,名为hello world项目
.
第三行导入IO(输入输出)
模块标准库并将其内容纳入范围。
导出函数的模块主要的
类型为主要
(定义见这个IO(输入输出)
标准库的模块)可以编译为独立可执行文件。例如:主要的 = 运行 (输入StrLn “你好,
世界!")
运行IO(输入输出)
命令输入StrLn “你好, 世界!"
和然后退出程序。
使用GHC后端编译
一旦我们在Emacs或Atom中加载了程序,我们就可以通过以下方法直接编译它紧迫的C-C公司 C-x公司 C-C公司
并进入GHC公司
。或者,我们可以打开终端会话,导航到顶级文件夹并运行:
这个--编译
此处的标志通过GHC后端计算机可以执行的顶级文件夹中的二进制文件。
最后,我们可以运行可执行文件(./hello-world程序
在Unix上系统,hello-world程序.exe
在Windows上)从命令行执行以下操作:
$ 光盘 <您的 顶层 文件夹>$ ./hello-world程序你好, 世界!
使用JavaScript后端编译
这个JavaScript后端翻译Agda的源代码hello world项目
文件转换为JavaScript代码。
从Emacs或Atom,按抄送 C-x公司 抄送
然后进入JS公司
编译模块到JavaScript。或者,打开终端会话,导航到顶级文件夹并运行:
这将创建几个.js文件
顶级文件夹中的文件。文件对应我们的源代码有名称jAgda.hello-world进度.js
.
提示
额外的--js优化
标志可以用于生成JavaScript代码速度更快,但可读性较差。此外--js-聪明
标志使生成的JavaScript代码更小、更均匀可读性较差。