带抽象

Conor McBride首先引入了抽象【McBride 2004】让我们通过有效地对中间计算结果进行模式匹配在函数的左侧添加一个额外的参数。

用法

在最简单的情况下具有构造只能用于区分中间计算的结果。例如

滤波器 : {A类: 设置}  (A类布尔) 列表A列表A过滤器p[]=[]滤波器p(x|x) 具有第x页过滤器p(x|x)    |真实的=x滤波器pxs滤波器p(x|x)    |=过滤器pxs

包含with-abstraction的子句没有右手边。相反,它后面是一些子句,左边有一个额外的参数,用竖线与原始参数分开(|).

当新子句中的原始参数相同时,可以使用...语法:

滤波器 : {A类: 设置}  (A类布尔) 列表A列表A过滤器p[]=[]过滤器p(x|x) 具有第x页...                  |真实的=x滤波器pxs...                  |=过滤器pxs

在这种情况下...扩展到滤波器 第页 (x) X轴)。有三种情况在这里你必须拼写出左手边:

  • 如果您想对原始参数进行进一步的模式匹配。
  • 当对中间结果进行模式匹配时其他参数(请参见点状图案).
  • 要消除与抽象嵌套的子句之间的歧义(请参见用抽象嵌套(见下文)。

概括

带抽象的力量来自这样一个事实:目标类型和原始参数的类型被概括为scrutine的值。请参见技术细节详情请参见下文。这种概括是当您必须证明使用定义的函数的属性时很重要具有例如,假设我们想证明滤波器功能以上满足某些性质。从的模式匹配开始列出我们得到的以下内容(孔中显示了目标类型)

假设:  {A类} 列表A 设置
假设p-无:P【】假设: 设置
假设q-nil(q-nil):
证明 : {A类: 设置} (第页:A类布尔) (X轴:列表A) (滤波器p xs)证明p[]= {!P[]!}证明p(x|x) = {!P(筛选器pxs|px)!}

在犯罪案件中,我们必须证明等待滤波器 第页 X轴 | 第页 x个.这是一个卡住了的抽象的语法-滤波器无法减少因为我们不知道第页 x个。此语法用于打印,但不接受为有效的Agda代码。现在如果我们在抽象结束第页 x个,但是不匹配我们得到的结果:

证明 : {A类: 设置} (第页:A类布尔) (X轴:列表A) (过滤器pxs)证明p[]=p-无证明p(x|x) 具有第x页...                 |第页= {!P(筛选器pxs|r)!}

这里是第页 x个目标类型中的已被变量替换第页为的结果而引入第页 x个.如果我们模式匹配第页这个with-clauses可以减少,给我们:

证明 : {A类: 设置} (第页:A类布尔) (X轴:列表A) (过滤器pxs)证明p[]=p-无证明p(x|x) 具有第x页...                 |真实的= {!P(xŞfilter P xs)!}
...                 |= {!P(过滤器pxs)!}

目标类型和其他参数的类型都是泛化的,所以如果参数的类型包含滤波器 第页 X轴.

证明 : {A类: 设置} (第页:A类布尔) (X轴:列表A) (过滤器pxs) 证明第[]页_=q为零证明第页(x|x)H(H)具有第x页...                    |真实的= {!H:P(过滤器pxs)!}
...                    |= {!H:P(x过滤器pxs)!}

概括并不局限于其他带有抽象的细节。全部目标类型和参数类型中出现的术语将为概括。

请注意,这种概括并不总是类型正确的,可能会导致(有时是隐秘的)类型错误。请参见带摘要的类型错误以下为更多详细信息。

用抽象嵌套

With-abstractions可以任意嵌套。唯一要记住的事这个案例是...语法适用于最接近的with-abstraction。例如,假设您想使用...在下面的定义中。

比较 :国家国家比较比较x y具有x<y比较x y|具有y<x比较x y||=平等的比较x y||真实的=更大的比较x y|真实的=较少的

你可能会想替换比较 x个 具有...在所有的用如下条款。

比较 :国家国家比较比较x y具有x<y...            |具有y<x...                       |=平等的...                       |真实的=更大的...            |真实的=较少的--错误

然而,这是错误的。在最后一句中...被解释为属于带抽象的内部(空白不被纳入账户),从而扩展到比较 x个 | | 真实的.在这种情况下,您必须拼写出左手边并写

比较 :国家国家比较比较x y具有x<y...            |具有y<x...                       |=平等的...                       |真实的=更大的比较x y|真实的=较少的

同时提取

您可以使用抽象在单个术语中对多个术语进行抽象。要做到这一点你用竖线分隔术语(|).

比较 :国家国家比较比较x y具有x<y|y<x...            |真实的|_=较少的...            |_|真实的=更大的...            ||=平等的

在本例中,抽象术语的顺序无关紧要,但一般而言确实如此。具体地说,后面的术语类型在值上进行了概括早期条款。例如

假设加号-调号: (a b类:国家) a+b≡b+a假设:国家 设置
三分之一 : (a b类:国家) (a+b) (b+a)thma b t公司具有a+b|加减a bthma b t公司|ab公司|等式= {!t:P ab,eq:ab≡b+a!}

请注意以及结果的类型等式属于加号-调号 b条已被概括 + b条。如果随着抽象被翻转,情况就不一样了。如果我们现在模式匹配打开等式我们得到

三分之一 : (a b类:国家) (a+b) (b+a)thma b t公司具有a+b|加号-调号a bthma b t公司| .(b+a) |回流= {!t:P(b+a)!}

从而可以用实际上,我们使用了交换性重写证明 + b条b条 + 类型为。这真是一个这样做很有用,因为它有特殊的语法。请参阅重写如下所示。

泛化的一个限制是,只有出现以下术语抽象时可见的被概括,但更多的实例一旦你开始填写右侧或在左侧进一步匹配。例如,考虑以下人为的我们需要匹配(f) n个对于类型q个减少,但我们想申请q个到一个引理(f) n个:

假设
  R(右)     : 设置
       :国家 设置
  (f)     :国家国家引理 : n个(f个n) R(右) :国家 设置Q零=Ş(例如) =(例如)
证明 : (n个:国家) (f个n) R(右)证明n q具有f个n证明n()   |证明n q|例如fn= {!q:P(例如fn)!}

一旦我们概括过来(f) n个我们不能再应用引理,它需要类型为的参数 (f) n)。为了解决此问题,我们可以添加抽象的引理:

证明 : (n个:国家) (f个n) R(右)证明n q具有f个n|引理n证明n()   ||_证明n q|例如fn|莱姆=勒姆q

在这种情况下引理 n个( (f) n) R(右))被概括为(f) n个所以在最后一条的右边我们有q个 : (例如 fn)莱姆 : (成功 fn) R(右).

请参见Inspect习语下面介绍了一种替代方法。

重写

记住以下示例同时提取从上面。

假设加号-调号: (a b类:国家) a+b≡b+a三分之一 : (a b类:国家) (a+b) (b+a)thma b t公司具有a+b|加减a bthma b t公司| .(b+a) |回流=

通过在方程式及其上进行抽象来重写方程式的这种模式左手边很常见,因此有特殊的语法:

三分之一 : (a b类:国家) (a+b) (b+a)thma b t公司重写加号-调号a b=

这个重写建造需要一个学期等式类型为左侧(lhs) 相对湿度,其中_≡_内置相等类型,并扩展为带有以下内容的摘要左侧(lhs)等式然后是匹配的结果等式反对回流:

英尺/秒重写等式=v(v)-->英尺/秒具有左侧(lhs)|等式...    | .相对湿度|回流=v(v)

一个限制重写结构就是你不能再做了参数的模式匹配之后重写,因为一切都发生了在一个子句中。然而,在重写之后,您可以使用抽象。对于实例,

假设T型:国家 设置

是偶数 :国家布尔等于零=真实的是偶数(真空零点) =是偶数(苏克(例如)) =是偶数n三分之一¦Β : (a b类:国家) T型(a+b) T型(b+a)三分之一a b t公司重写加号-调号a b具有甚至是一个三分之一a b t公司|真实的=三分之一a b t公司|=

请注意,重写引入的with-abstracted参数(左侧(lhs)等式)在代码中不可见。

视察成语

当你用抽象词你失去了表示其值的新参数。只要所有实例都可以属于您关心通过抽象进行概括,但正如我们所看到的在上面情况并非总是如此。在这个例子中,我们使用了同时抽象,以确保我们确实捕获了我们的所有实例需要。另一种方法是使用检查习语,它保留了证明原始术语等于其抽象的证据。

在最简单的形式中,inspect习惯用法使用单例类型:

数据辛格尔顿{} {A类: 设置} (x个:A类) : 设置哪里
  _带有≡_ : (:A类) x≡y单例x检查 :  {} {A类: 设置} (x个:A类) 单例x检查x=x个具有≡ref

现在不用抽象了,您可以抽象检查 。对于实例,

滤波器 : {A类: 设置}  (A类布尔) 列表A列表A过滤器p[]=[]过滤器p(x|x) 具有检查(第x页)
...                  |真实的具有选择当量= {!eq:px≡true!}
...                  |具有≡eq= {!eq:px≡false!}

这是我们得到的证据第页 x个 真实的第页 x个 在各自的我们可以使用正确的分支。注意,由于with-abstraction是结束检查 (第页 x)而不是第页 x个,目标和参数类型为否更长的概括第页 x个。要修复此问题,我们可以替换单例类型按如下功能图类型(参见匿名模块学习关于使用模块将类型参数绑定到图表检查):

模块_ {b条} {A类: 设置} {B类:A类 设置b条} 哪里

  数据图表((f): x个B个x) (x个:A类) (:B个x) : 设置b条哪里
    内窥镜 :f x≡y图f x y检查 : ((f): x个B个x) (x个:A类) 图f x(f x(f x))检查__=内窥镜反射

将其用于术语 v(v)你对这两件事都很抽象 v(v)检查 v(v)例如,将此应用到上面的示例中,我们得到

假设
  R(右)     : 设置
       :国家 设置
  (f)     :国家国家引理 : n个(f个n) R(右) :国家 设置Q零=Ş(例如) =(例如)

证明 : (n个:国家) (f个n) R(右)证明nq具有f个n|检查f n证明n()   ||_证明n q|例如fn|英格拉夫当量= {!q:P(suc-fn),eq:f n≡suc-fm!}

然后我们可以用证据证明(f) n个 苏克 fn公司申请引理q个.

这个版本的inspect习语是定义的(使用稍微不同的名称)在中标准库在模块中关系。二元的。命题等式和在agda-prelude公司在里面前奏曲。平等。检查(由重新出口前奏曲).

带抽象的替代方案

虽然带抽象非常强大,但在某些情况下,您无法或不想使用它。例如,如果您是在右侧的表达式中。在这种情况下,有几个选择。

lambdas图案

Agda没有原语案例构造,但可以模拟一个使用模式匹配lambdas。首先定义功能案例(共个)_如下:

案例(_O)_ :  {a b类} {A类: 设置} {B类: 设置b条} A类 (A类B类) B类f的情况x=f x(f x)

然后,您可以将此函数与模式匹配的lambda用作第二个获取Haskell样式case表达式的参数:

滤波器 : {A类: 设置}  (A类布尔) 列表A列表A过滤器p[]=[]过滤器p(x|x) =案例p x,共个λ{真实的x滤波器pxs; 过滤器pxs}

此版本的案例(_O)_仅适用于非依赖函数。对于依赖函数-目标类型在大多数情况下是不可推断的,但是可以将变量与显式B类对于这种情况:

案例返回_ :  {a b类} {A类: 设置} (x个:A类) (B类:A类 设置b条)  (x个B个x) B个xcase x返回B of f=f x(f x)

依赖版本将允许您对scrutine进行概括,就像使用抽象,但必须手动执行。它不允许你做的两件事是

  • 左侧参数的进一步模式匹配,以及
  • 通过case表达式中的模式细化左侧的参数。对于实例,如果您匹配Vec公司 A类 n个这个n个将由零和缺点模式。

助手函数

内部with-abstractions转换为辅助函数(请参见技术细节下面),您可以随时[1]写下这些手动运行。缺点是helper的类型签名函数需要显式写出,但幸运的是Emacs模式有一个命令(抄送 C-小时)使用相同的生成with-function类型的算法。

性能注意事项

这个概括步骤with-abstraction需要规范scrutine、目标和参数类型,确保概括了scrutine的实例。概括还需要进行类型检查以确保它不是问题类型。这使得键入带有抽象的check a非常昂贵如果

  • 正常化代价高昂,
  • 目标和参数类型的规范化形式很大,这使得发现那些极其昂贵的例子,
  • 类型检查泛化是昂贵的,因为类型很大,或者因为检查它们需要大量的计算。

在这些情况下,值得查看带抽象的替代方法从上面。

技术细节

内部with-abstractions被转换为辅助函数–有中没有带摘要的核心语言。此翻译进行如下如下。给出带抽象的

\[\arraycolsep=1.4pt\开始{array}{lrllcl}\多列{3}{l}{f:\Gamma\到B}\\f~ps&\mathbf{with}~&t1&&\ldots&~tm\\f~ps_1&~&q{11}&&\ldots&~q{1m}&=v_1\\\视频短片\\f~psn&~&q{n1}&&\ldots&~q{nm}&=vn\结束{数组}\]

哪里\增量\vdash ps:\Gamma(即。\三角洲键入绑定的变量秒),我们

  • 推断竞赛的类型t1:A_1,\ldot,t_m:A_m.

  • 划分上下文\三角洲进入之内\德尔塔_1\增量_2这样的话\增量_1是最小的上下文,其中\Delta_1\vdash t_i:A_i为所有人我也就是说,在这里,比赛类型很好。请注意,分区不需要进行拆分,\增量_1\增量_2可以是(形式良好的)重新排序\三角洲.

  • 时间(TI)s、 通过计算

    C=(w_1:A_1)(w_1:A_2')\ldot(w_m:A_m')\ to \Delta_2'\ to B'

    这样C类不包含任何时间

    A_i’[w_1:=t1\ldots w_{i-1}:=t_{i-1{]\simeq A_i(\Delta_2'\至B')[w_1:=t1\ldots w_m:=t_m]\simeq\Delta_2 \至B

    哪里X \模拟Y是标准形式的相等X(X)Y(Y)辅助功能的类型为\增量_1\至C.

  • 检查一下\增量_1\至C类型正确,但不保证正确(请参见在下面).

  • 添加函数f{辅助},与相互递归(f),使用定义

    \[\arraycolsep=1.4pt\开始{数组}{lll}\多列{4}{l}{f_{aux}:\Delta_1\到C}\\f{aux}~ps{11}&\mathit{qs}_1&ps{21}&=v_1\\\视频短片\\f{aux}~ps{1n}&\mathit{qs}n&ps{2n}&=v_n\\\结束{数组}\]

    哪里\马西特{qs}_i=q_{i1}\ldots q_{im}、和ps_{1i}:\增量_1ps_{2i}:\增量_2这些图案是来自功率因数i对应于变量秒。请注意,由于划分\三角洲进入之内\增量_1\增量_2,图案ps{1i}ps_{2i}可以是不同的顺序从他们的表现来看功率因数i.

  • 通过调用将替换为抽象f{辅助}导致最终定义

    \[\arraycolsep=1.4pt\开始{数组}{l}f:\Gamma\到B\\f~ps=f{aux}~\mathit{xs}_1~ts~\mathit{xs}_2\结束{数组}\]

    哪里ts=t1 \ldot t_m\松饼{xs}_1\松饼{xs}_2变量来自\三角洲对应于\德尔塔_1\增量_2分别是。

示例

下面是一些with-abstractions及其翻译的示例。

假设
   A类     : 设置
   _+_   :A类A类A类T型     :A类 设置
   mkT公司   : x个T x(T x)     : x个T x(T x) 设置

--with参数的类型A没有自由变量,因此with
--首先是争论
(f)¦Β : (x年:A类) (:T型(x+y)) T型(x+y)(f)x年t具有x+y(f)x年t|w个= {!!}

--使用函数生成
f辅助¦Β : (w个:A类) (x年:A类) (:T重量) T重量f辅助宽x厚= {!!}

--输入with参数不需要x和p,因此上下文
--在with参数之前仅用y重新排序
(f) : (x年:A类) (第页:同比(mkT年)) 同比(mkT年)(f)x y p(x y p)具有mkT年(f)x y p(x y p)|w个= {!!}

f辅助 : (:A类) (w个:) (x个:A类) (第页:同比) 同比f辅助y宽x p= {!!}

假设
  H(H) : x年T型(x+y)  设置

--带参数的多个始终插入在一起,因此在本例中
--t最后出现在左边,因为需要输入h,因此x+y不是
--从t型中抽象出来
(f) : (x年:A类) (:T型(x+y)) (小时:高x厚) T型(x+y)(f)x年t小时具有x+y|小时(f)x年t小时|w个¦Β|w个= {!t:t(x+y),目标:t w₁ !}

f辅助 : (x年:A类) (:T型(x+y)) (小时:高x厚) (w个¦Β:A类) (w个:高x厚) T重量¦Βf辅助x y t h ww个= {!!}

--但前面的带参数是从后面的类型中抽象出来的
(f) : (x年:A类) (:T型(x+y)) T型(x+y)(f)x年t具有x+y|(f)x年t|w个¦Β|w个= {!t:t(x+y),w₂ : T重量₁, 目标:T w₁ !}

f辅助 : (x年:A类) (:T型(x+y)) (w个¦Β:A类) (w个:T重量¦Β) T重量¦Βf辅助x年t月w个= {!!}

带摘要的类型错误

如上所述,泛化并不总是产生类型良好的结果。当您对出现在类型子团队的目标或参数类型的。最简单的示例是对相关对的第一个分量。例如,

假设
  A类 : 设置
  B类 :A类 设置
  H(H) : (x个:A类) B个x 设置
坏的,坏的 : (第页:∑A B) H(H)(fst压力) (信噪比)带p的坏具有fst压力...           |_= {!!}

这里,概括一下有限状态试验 第页生成ill类型的应用程序H(H) w个 (瑞士) 第页)您将得到以下类型错误:

fst p!=A型w当检查类型(p:∑A B)(w:A)H w(snd p),共用函数生成的格式良好

此消息可能有点难以解释,因为它只打印眼前的问题(有限状态试验 第页 != w个)和功能齐全。收件人获取更详细的错误,指向类型中错误是,您可以从错误消息中复制并粘贴with-function类型并尝试单独键入检查。

[1]终止检查程序有特殊处理带功能,因此替换具有等效的helper函数可能无法终止。
[McBride2004]C.McBride和J.McKinna。左侧视图《函数编程杂志》,2004年。http://strictlypositive.org/vfl.pdf.