归纳类型定义中的“受保护”否定事件总是不好的吗?


11

我知道某些负面事件肯定会很糟糕:

data False

data Bad a = C (Bad a -> a)

selfApp :: Bad a -> a
selfApp (x@(C x')) = x' x

yc :: (a -> a) -> a
yc f = selfApp $ C (\x -> f (selfApp x))

false :: False
false = yc id

但是,我不确定是否:

  • 所有带有负面发生的归纳类型都可能出错;

  • 如果是这样,有一种已知的机械方法;

例如,我一直在努力尝试使这种类型出错:

type Not a = a -> False

data Bad2 a = C2 (Bad2 (Not a) -> a)

任何有关该主题文献的指导将不胜感激。


1
这是Coq吗?哈斯克尔?伪型理论?“错”是什么意思?
戴夫·克拉克

@DaveClarke对不起,代码是Haskell,但更多的是关注Coq或Agda等禁止出现负面事件的语言。所谓“出错”,是指能够写出歧义的术语,从而能够像在Haskell的示例中一样居住False。
太平洋

Answers:


10

可以通过与Knaster-Tarski定理类似的方式来理解禁止出现负数的原因。这个定理说

如果是一个完整的晶格和˚F 大号大号是一个单调函数大号,然后该组固定点的˚F也是一个完整的晶格。特别地,有一个至少固定点μ ˚F和一个最大的不动点ν ˚F大号F大号大号大号FμFνF

在传统的模型理论,棱可以被看作是命题,和顺序关系p q可以理解为蕴涵(即,q大号pqq的真理由entailed 的真理)。p

当我们从模型理论转向证明理论时,晶格泛化为类别。类型可以看作是一个类别的对象和地图ë P Q代表一个证明Q可以衍生自CËP

当我们尝试解释由递归方程定义的类型时,ee,,很明显要做的是寻找Knaster-Tarski定理的推广。因此,我们知道需要一个仿函数 F CC,而不是晶格上的单调函数,该函子将对象发送到对象,但是推广了单调性条件,因此每个映射 e P ñ=μα1个+α FCC都获得映射 F e F P F Q (相干条件是 F将身份发送给身份并保存构图,使得 FËPFËFPFF)。FGF=FGFF

因此,如果您要归纳数据类型 ,还需要为类型运算符 F提供根据项的函数μαFαF以确保所需的不动点存在。Agda和Coq中的严格积极性条件是一种语法条件,暗含了这种语义约束。松散地说,它表示,如果您使用总和和乘积构建一个类型运算符,那么您始终可以完善函数功能,因此以这种方式形成的任何类型都应有一个固定点。

在依赖类型的语言中,您还具有索引和参数化的类型,因此您的实际任务更加复杂。Bob Atkey(谁写了这个博客这里这里)告诉我,寻找这个故事的好地方是:

正如安德烈(Andrej)所指出的,从根本上说,否定的发生是否可行取决于类型理论的模型。基本上,当您具有递归定义时,您正在寻找一个固定点,并且数学中有很多固定点定理。

我个人经常使用的一个是Banach的不动点定理,它说,如果您在度量空间上具有严格的收缩函数,那么它就具有唯一的不动点。这个想法由(IIRC)莫里斯·尼瓦特(Maurice Nivat)引入语义中,并由美国和鲁滕(Rutten)进行了广泛研究,最近,伯克达尔(Birkedal)和他的合作者将这种想法与一种流行的操作技术“步进索引”联系在一起。

这就产生了类型理论,其中允许在递归类型中出现否定事件,但仅当否定事件在特殊的“保护”类型构造函数下发生时才出现。中野浩史(Hiroshi Nakano)提出了这个想法,与我自己和尼克·本顿(Nick Benton)以及拉斯·伯克达尔(Lars Birkedal)和他的合著者都建立了与Banach定理的联系。


7

有时,您可以“靠运气”来求解递归方程。

我想你想这样做的套(相对于某种领域的理论值)。如果我们展开你的定义,并写下方程式的情况下直接Haskell的注解,我们得到 让我们考虑两种情况:

一种一种一种
  1. 如果有人居住,也就是说,它包含的东西,然后→交通,因此方程简化为→交通1.事实上,单身设置1个求解方程。一种一种

    一种一种1。
    1个
  2. 一种1个

结论:有两种解决方案,空类型(您称为False)和单位类型()

一种一种22
data Cow a = Moo ((a -> Bool) -> Bool)

一种22一种一种22一种

ñ22ñ
2ñ22ñInteger(Integer -> Bool) -> Bool

3

很难在安德烈(Andrej)或尼尔(Neel)的解释中添加任何内容,但我会试一试。我将尝试从句法观点出发,而不是试图揭示底层语义,因为解释是更基本的,并且我将为您的问题提供更直接的答案。

λ微积分中而不是在Haskell基础上的更复杂的系统中工作。我特别相信类型变量的存在可能会在一定程度上使您感到困惑。

关键参考如下:

Mendler,N。(1991)。二阶λ演算中的归纳类型和类型约束。恐怕我没有在网上找到参考。但是,这些声明和证明可以在Nax的博士学位论文中找到(强烈建议阅读!)。

一种d

一种d=一种d一种

一种

λX一种dX X一种d一种

所以

λX一种dX X λX一种dX X一种

一种d=F一种d
FXXFX

当然,您不是在使用方程式定义的类型,而是在使用构造函数,即

data Bad = Pack (Bad -> A)

而不是严格的平等。但是你可以定义

unpack :: Bad -> (Bad -> A)
unpack (Pack f) = f

这足以使该结果继续成立:

 (\x:Bad -> unpack x x) (Pack (\x:Bad -> unpack x x))

一种


在第二个示例中,事情有些棘手,因为您有一些类似的东西

一种d=一种d一种

一种d一种d一种d 一种一种d ñØŤ 一种

type Not a = a -> False

data Not a = Not a

如果Haskell允许这样的类型定义,将很容易解决:

type Acc = Not Acc

在这种情况下,您可以按照与以前完全相同的方式构建循环组合器。我怀疑您可以使用以下方法进行类似(但更复杂)的构造

data Acc = D (Not Acc)

这里的问题是要建立同构

Bad Acc <-> Bad (Not Acc)

您必须处理混合方差。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.