Понимание синтаксиса 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, хотя это было около года назад.


person Brendan Moore    schedule 23.01.2015    source источник


Ответы (1)


Начнем с постулата. Синтаксис прост:

postulate name : type

Это утверждает, что существует некоторое значение типа type, называемое name. Думайте об этом как об аксиомах логики - о вещах, которые определены как истинные и не подвергаются сомнению (в данном случае со стороны Агды).


Далее идет определение данных. В объявлении mixfix есть небольшая недоработка, поэтому я исправлю его и объясню, что он делает. Первая строка:

data _∨_ (A B : Set) : Set where

Представляет новый тип (конструктор) под названием _∨_. _∨_ принимает два аргумента типа Set и затем возвращает Set.

Я сравню с Haskell. A и B более или менее эквивалентны a и b в следующем примере:

data Or a b = Inl a | Inr b

Это означает, что определение данных определяет полиморфный тип (шаблон или универсальный, если хотите). Set - это Agda-эквивалент * в Haskell.


Что случилось с подчеркиванием? Agda позволяет вам определять произвольные операторы (префикс, постфикс, инфикс ... обычно просто вызываются одним именем - mixfix). Подчеркивание просто указывает Agda на аргументы. Лучше всего это видно с операторами префикса / постфикса:

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

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

Вы даже можете создавать сумасшедшие операторы, такие как:

if_then_else_ : ...

Следующая часть - определение самих конструкторов данных. Если вы видели GADT от Haskell, это более или менее то же самое. Если у вас нет:

Когда вы определяете конструктор в Haskell, скажем Inr выше, вы просто указываете тип аргументов, а Haskell определяет тип всего объекта, то есть Inr :: b -> Or a b. Когда вы пишете GADT или определяете типы данных в Agda, вам нужно указать весь тип (и для этого есть веские причины, но я не буду сейчас вдаваться в подробности).

Итак, в определении данных указаны два конструктора: inl типа A → A ∨ B и inr типа B → A ∨ B.


Теперь самое интересное: первая строка classical-2 - это простое объявление типа. Что случилось с Set? Когда вы пишете полиморфные функции в Haskell, вы просто используете строчные буквы для представления переменных типа, например:

id :: a -> a

На самом деле вы имеете в виду:

id :: forall a. a -> a

И что вы на самом деле имеете в виду:

id :: forall (a :: *). a -> a

Т.е. это не просто какой-то a, но этот a тип. Agda заставляет вас сделать этот дополнительный шаг и явно объявить эту количественную оценку (это потому, что вы можете количественно оценить больше вещей, чем просто типы).

А фигурные скобки? Позвольте мне снова использовать приведенный выше пример Haskell. Когда вы где-то используете функцию id, скажем id 5, вам не нужно указывать это a = Integer.

Если бы вы использовали обычные партезы, вам нужно было бы указывать фактический тип A каждый раз, когда вы вызываете classical-2. Однако в большинстве случаев тип может быть определен из контекста (как и в примере id 5 выше), поэтому в таких случаях вы можете «скрыть» аргумент. Затем Agda пытается заполнить это автоматически - и, если не может, жалуется.


И последняя строка: λ x → y - это способ выражения Agda \x -> y. Это должно объяснить большую часть линии, единственное, что осталось, - это фигурные скобки. Я почти уверен, что вы можете опустить их здесь, но в любом случае: скрытые аргументы делают то, что говорят - они скрывают. Поэтому, когда вы определяете функцию от {A} до B, вы просто предоставляете что-то типа B (потому что {A} скрыт). В некоторых случаях вам нужно знать значение скрытого аргумента, и именно это делает этот особый вид лямбда: λ {A} → позволяет вам получить доступ к скрытому A!

person Vitus    schedule 23.01.2015