理解Agda的语法

时间:2015-01-23 00:33:42

标签: syntax agda

使用以下作为示例

postulate DNE : {A : Set} → ¬ (¬ A) → A

data ∨ (A B : Set) : Set where
  inl : A → A ∨ B
  inr : B → A ∨ B

-- Use double negation to prove exclude middle

classical-2 : {A : Set} → A ∨ ¬ A
classical-2 = λ {A} → DNE (λ z → z (inr (λ x → z (inl x)))

我知道这是正确的,纯粹是因为agda是如何工作的,但我是这种语言的新手并且不能理解它的语法是如何工作的,如果有人能指引我完成任务,我将不胜感激在,谢谢:))

我有haskell的经验,虽然那是一年前的事。

1 个答案:

答案 0 :(得分:6)

让我们从假设开始。语法很简单:

postulate name : type

这断言存在一些名为type的{​​{1}}类型的值。把它想象成逻辑中的公理 - 被定义为真实且不被质疑的事物(在这种情况下由Agda提出)。


接下来是数据定义。 mixfix声明略有疏忽,所以我会解决它并解释它的作用。第一行:

name

引入一个名为data _∨_ (A B : Set) : Set where 的新类型(构造函数)。 _∨_接受_∨_类型的两个参数,然后返回Set

我将它与Haskell进行比较。在以下示例中,SetA或多或少等同于Ba

b

这意味着数据定义定义了多态类型(模板或通用,如果您愿意)。 data Or a b = Inl a | Inr b 是Haskell Set的Agda等价物。


下划线有什么用? Agda允许您定义任意运算符(前缀,后缀,中缀...通常仅由单个名称 - mixfix调用)。下划线告诉Agda参数在哪里。使用前缀/后缀运算符最好看到这一点:

*

您甚至可以创建疯狂的运算符,例如:

-_ : Integer → Integer   -- unary minus
- n = 0 - n

_++ : Integer → Integer   -- postfix increment
x ++ = x + 1

下一部分是数据构造函数本身的定义。如果您已经看过Haskell的GADT,这或多或少是相同的。如果你还没有:

当你在Haskell中定义一个构造函数时,比如上面的if_then_else_ : ... ,你只需指定参数的类型,Haskell就会找出整个事物的类型,即Inr。当您在Agda中编写GADT或定义数据类型时,您需要指定整个类型(并且有充分的理由,但我现在不会介绍它。)

因此,数据定义指定了两个构造函数Inr :: b -> Or a binl类型和A → A ∨ B类型inr


现在出现了有趣的部分:B → A ∨ B的第一行是一个简单的类型声明。 classical-2的事情是怎么回事?当您在Haskell中编写多态函数时,您只需使用小写字母来表示类型变量,例如:

Set

你的真正含义是:

id :: a -> a

真正的意思是:

id :: forall a. a -> a

即。它不仅仅是id :: forall (a :: *). a -> a ,而是a是一种类型。 Agda让你做这个额外的步骤并明确地声明这个量化(这是因为你可以量化更多的东西而不仅仅是类型)。

花括号?让我再次使用上面的Haskell示例。当您在某处使用a函数时,请说id,您无需指定id 5

如果您使用普通的paretheses,则每次调用a = Integer时都必须提供实际类型A。但是,大多数情况下,类型可以从上下文中推断出来(很像上面的classical-2示例),因此对于这些情况,您可以“隐藏”参数。然后Agda尝试自动填写 - 如果不能,它会抱怨。


最后一行:id 5是Agda说λ x → y的方式。这应该可以解释大部分内容,唯一剩下的就是花括号了。我相当肯定你可以在这里省略它们,但无论如何:隐藏的论据做他们所说的 - 他们隐藏。因此,当您定义从\x -> y{A}的函数时,您只需提供B类型的内容(因为隐藏了B)。在某些情况下,您需要知道隐藏参数的值,这就是这种特殊的lambda所做的事情:{A}允许您访问隐藏的A!