尝一尝阿格达

本节的目的是通过一些小例子。第一个是依赖类型的演示编程,第二个演示如何使用Agda作为校对助手。最后,我们构建一个完整的程序并用GHC将其编译为可执行程序和Javascript后端。

前期工作

继续之前,请确保已安装Agda和兼容版本的标准库.

Agda项目通常是开发的以交互方式,这意味着可以键入尚未完成但包含“洞”,以后可以填充。支持的编辑Agda项目的交互式开发包括通过Emacs模式,Atom通过Atom的agda模式,通过Visual Studio代码VSCode的agda模式和Vim viaagda-vim公司.

提示

如果您想在不安装Agda的情况下偷看它,请尝试Agda Pad公司

注释

在本简介中,我们使用了Agda的几个交互式命令从typechecker获取信息并操作代码带孔。下面是将在此中使用的命令列表辅导的:

  • 抄送 C-l公司:加载文件并键入选中它。

  • C-C公司 C-d公司:推导给定表达式的类型。

  • 抄送 C-n公司:规范化给定表达式。

  • 抄送 C-,:显示当前孔中预期的类型任何局部变量的类型。

  • 抄送 抄送:给定变量的大小写拆分。

  • 抄送 C-SPC公司:用给定表达式替换孔(如果有)正确的类型。

  • 抄送 C-r公司:通过将其替换为给定的表达式应用于适当数量的新孔。

  • 抄送 C-x公司 抄送(C-x公司 抄送(在VS代码中):编译Agda程序。

请参见组合键的符号获取完整列表交互式命令(键绑定)。

使用相关类型编程:向量

在下面的代码中,我们对向量(在计算机的意义上科学,而不是数学意义上的)。大致来说,向量是具有确定长度的对象列表。

模块 你好-世界环保组织 哪里

打开 进口 数据。国家 使用 (ℕ; 零; 成功)

数据 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文件有一个顶层模块其名称必须与文件名匹配,并且零个或多个嵌套模块。每个模块都包含一个列表声明。此示例有一个名为你好,世界各地,它有三个声明:

  1. 打开 进口导入数据类型的语句及其施工人员成功来自模块数据。国家并将其纳入范围,

  2. 一个数据定义数据类型的声明Vec公司具有两个构造函数:空向量构造函数[]这个欺骗建造师_∷_

  3. 最后是一个中缀声明指定优先对于欺骗操作。

提示

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个对象:数字0n-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使用其交互模式帮助我们编写证明。首先,我们首先写一个简单的子句,这样即使我们仍然可以加载文件不知道证据。该子句由属性名称、输入变量,等于符号=和问号?.

+-防联想的 x个  z = ?

当我们重新加载文件时,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编写与世界互动的程序。在本节中,我们用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公司。或者,我们可以打开终端会话,导航到顶级文件夹并运行:

阿格达 --编译 hello world项目

这个--编译此处的标志通过GHC后端计算机可以执行的顶级文件夹中的二进制文件。

最后,我们可以运行可执行文件(./hello-world程序在Unix上系统,hello-world程序.exe在Windows上)从命令行执行以下操作:

$ 光盘 <您的 顶层 文件夹>$ ./hello-world程序你好, 世界!

使用JavaScript后端编译

这个JavaScript后端翻译Agda的源代码hello world项目文件转换为JavaScript代码。

从Emacs或Atom,按抄送 C-x公司 抄送然后进入JS公司编译模块到JavaScript。或者,打开终端会话,导航到顶级文件夹并运行:

阿格达 --js型 hello world项目

这将创建几个.js文件顶级文件夹中的文件。文件对应我们的源代码有名称jAgda.hello-world进度.js.

提示

额外的--js优化标志可以用于生成JavaScript代码速度更快,但可读性较差。此外--js-聪明标志使生成的JavaScript代码更小、更均匀可读性较差。

接下来该去哪里?

有很多关于Agda的书籍和教程。我们建议这样做教程列表.

加入Agda社区!

联系并加入Agda社区,或加入上的对话阿格达·祖利普.