Arity-универсальное программирование в Agda

Как написать универсальные функции в Agda? Можно ли написать полностью зависимые и универсальные полиморфные функции общего типа?

1 ответ

Решение

Я возьму n-арную композиционную функцию в качестве примера.

Самая простая версия

open import Data.Vec.N-ary

comp : ∀ n {α β γ} {X : Set α} {Y : Set β} {Z : Set γ}
     -> (Y -> Z) -> N-ary n X Y -> N-ary n X Z
comp  0      g y = {!!}
comp (suc n) g f = {!!}

Вот как N-ary определяется в Data.Vec.N-ary модуль:

N-ary : ∀ {ℓ₁ ℓ₂} (n : ℕ) → Set ℓ₁ → Set ℓ₂ → Set (N-ary-level ℓ₁ ℓ₂ n)
N-ary zero    A B = B
N-ary (suc n) A B = A → N-ary n A B

Т.е. comp получает номер n, функция g : Y -> Z и функция f, который имеет arity n и результирующий тип Y,

в comp 0 g y = {!!} случай у нас есть

Goal : Z
y    : Y
g    : Y -> Z

следовательно, отверстие может быть легко заполнено g y,

в comp (suc n) g f = {!!} дело, N-ary (suc n) X Y сводится к X -> N-ary n X Y а также N-ary (suc n) X Z сводится к X -> N-ary n X Z, Итак, мы имеем

Goal : X -> N-ary n X Z
f    : X -> N-ary n X Y
g    : Y -> Z

Cc Cr уменьшает отверстие до λ x -> {!!}, и сейчас Goal : N-ary n X Z, который может быть заполнен comp n g (f x), Таким образом, все определение

comp : ∀ n {α β γ} {X : Set α} {Y : Set β} {Z : Set γ}
     -> (Y -> Z) -> N-ary n X Y -> N-ary n X Z
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

Т.е. comp получает n аргументы типа X, применяется f к ним, а затем относится g к результату.

Простейшая версия с зависимой g

когда g имеет тип (y : Y) -> Z y

comp : ∀ n {α β γ} {X : Set α} {Y : Set β} {Z : Y -> Set γ}
     -> ((y : Y) -> Z y) -> (f : N-ary n X Y) -> {!!}
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

что там должно быть в дыре? Мы не можем использовать N-ary n X Z как и раньше, потому что Z теперь это функция. Если Z это функция, мы должны применить ее к чему-то, что имеет тип Y, Но единственный способ получить что-то типа Y это применить f в n аргументы типа X, Который так же, как наш comp но только на уровне типа:

Comp : ∀ n {α β γ} {X : Set α} {Y : Set β}
     -> (Y -> Set γ) -> N-ary n X Y -> Set (N-ary-level α γ n)
Comp  0      Z y = Z y
Comp (suc n) Z f = ∀ x -> Comp n Z (f x)

А также comp тогда это

comp : ∀ n {α β γ} {X : Set α} {Y : Set β} {Z : Y -> Set γ}
     -> ((y : Y) -> Z y) -> (f : N-ary n X Y) -> Comp n Z f
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

Версия с аргументами разных типов

Существует статья " Arity-generic datatype-generic Программирование ", в которой, помимо прочего, описывается, как писать функции arity-generic, которые получают аргументы разных типов. Идея состоит в том, чтобы передать вектор типов в качестве параметра и сложить его в значительной степени в стиле N-ary:

arrTy : {n : N} → Vec Set (suc n) → Set
arrTy {0}     (A :: []) = A
arrTy {suc n} (A :: As) = A → arrTy As

Однако Агда не может вывести этот вектор, даже если мы предоставим его длину. Следовательно, документ также предоставляет оператор для каррирования, который делает из функции, которая явно получает вектор типов, функцию, которая получает n неявные аргументы.

Этот подход работает, но он не масштабируется до полностью универсальных полиморфных функций. Мы можем избежать всех этих проблем, заменив Vec тип данных с _^_ оператор:

_^_ : ∀ {α} -> Set α -> ℕ -> Set α
A ^ 0     = Lift ⊤
A ^ suc n = A × A ^ n

A ^ n изоморфен Vec A n, Тогда наш новый N-ary является

_->ⁿ_ : ∀ {n} -> Set ^ n -> Set -> Set
_->ⁿ_ {0}      _      B = B
_->ⁿ_ {suc _} (A , R) B = A -> R ->ⁿ B

Все типы лежат в Set для простоты. comp сейчас

comp : ∀ n {Xs : Set ^ n} {Y Z : Set}
     -> (Y -> Z) -> (Xs ->ⁿ Y) -> Xs ->ⁿ Z
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

И версия с зависимой g:

Comp : ∀ n {Xs : Set ^ n} {Y : Set}
     -> (Y -> Set) -> (Xs ->ⁿ Y) -> Set
Comp  0      Z y = Z y
Comp (suc n) Z f = ∀ x -> Comp n Z (f x)

comp : ∀ n {Xs : Set ^ n} {Y : Set} {Z : Y -> Set}
     -> ((y : Y) -> Z y) -> (f : Xs ->ⁿ Y) -> Comp n Z f
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

Полностью зависимый и универсальный полиморфный comp

Основная идея состоит в том, чтобы представлять вектор типов как вложенные зависимые пары:

Sets : ∀ {n} -> (αs : Level ^ n) -> ∀ β -> Set (mono-^ (map lsuc) αs ⊔ⁿ lsuc β)
Sets {0}      _       β = Set β
Sets {suc _} (α , αs) β = Σ (Set α) λ X -> X -> Sets αs β

Второй случай читается как "есть тип X так что все другие типы зависят от элемента X ". Наш новый N-ary тривиально:

Fold : ∀ {n} {αs : Level ^ n} {β} -> Sets αs β -> Set (αs ⊔ⁿ β)
Fold {0}      Y      = Y
Fold {suc _} (X , F) = (x : X) -> Fold (F x)

Пример:

postulate
  explicit-replicate : (A : Set) -> (n : ℕ) -> A -> Vec A n

test : Fold (Set , λ A -> ℕ , λ n -> A , λ _ -> Vec A n) 
test = explicit-replicate

Но каковы типы Z а также g сейчас?

comp : ∀ n {β γ} {αs : Level ^ n} {Xs : Sets αs β} {Z : {!!}}
     -> {!!} -> (f : Fold Xs) -> Comp n Z f
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

Напомним, что f ранее имел тип Xs ->ⁿ Y, но Y теперь скрыто в конце этих вложенных зависимых пар и может зависеть от элемента любого X от Xs, Z ранее имел тип Y -> Set γ следовательно, теперь нам нужно добавить Set γ в Xs, делая все x с неявным:

_⋯>ⁿ_ : ∀ {n} {αs : Level ^ n} {β γ}
      -> Sets αs β -> Set γ -> Set (αs ⊔ⁿ β ⊔ γ)
_⋯>ⁿ_ {0}      Y      Z = Y -> Z
_⋯>ⁿ_ {suc _} (_ , F) Z = ∀ {x} -> F x ⋯>ⁿ Z

в порядке, Z : Xs ⋯>ⁿ Set γ какой тип имеет g? Ранее это было (y : Y) -> Z y, Снова нам нужно добавить что-то к вложенным зависимым парам, так как Y снова скрыт, только теперь зависимым образом:

Πⁿ : ∀ {n} {αs : Level ^ n} {β γ}
   -> (Xs : Sets αs β) -> (Xs ⋯>ⁿ Set γ) -> Set (αs ⊔ⁿ β ⊔ γ)
Πⁿ {0}      Y      Z = (y : Y) -> Z y
Πⁿ {suc _} (_ , F) Z = ∀ {x} -> Πⁿ (F x) Z

И наконец

Comp : ∀ n {αs : Level ^ n} {β γ} {Xs : Sets αs β}
     -> (Xs ⋯>ⁿ Set γ) -> Fold Xs -> Set (αs ⊔ⁿ γ)
Comp  0      Z y = Z y
Comp (suc n) Z f = ∀ x -> Comp n Z (f x)

comp : ∀ n {β γ} {αs : Level ^ n} {Xs : Sets αs β} {Z : Xs ⋯>ⁿ Set γ}
     -> Πⁿ Xs Z -> (f : Fold Xs) -> Comp n Z f
comp  0      g y = g y
comp (suc n) g f = λ x -> comp n g (f x)

Тест:

length : ∀ {α} {A : Set α} {n} -> Vec A n -> ℕ
length {n = n} _ = n

explicit-replicate : (A : Set) -> (n : ℕ) -> A -> Vec A n
explicit-replicate _ _ x = replicate x

foo : (A : Set) -> ℕ -> A -> ℕ
foo = comp 3 length explicit-replicate

test : foo Bool 5 true ≡ 5
test = refl

Обратите внимание на зависимость в аргументах и ​​результирующий тип explicit-replicate, Кроме того, Set заключается в Set₁, в то время как а также A роды Set - это иллюстрирует полиморфизм вселенной.

замечания

AFAIK, нет понятной теории для неявных аргументов, поэтому я не знаю, как все это будет работать, когда вторая функция (т.е. f) получает неявные аргументы. Этот тест:

foo' : ∀ {α} {A : Set α} -> ℕ -> A -> ℕ
foo' = comp 2 length (λ n -> replicate {n = n})

test' : foo' 5 true ≡ 5
test' = refl

передается по крайней мере.

comp не может обрабатывать функции, если от значения зависит юниверс, в котором находится некоторый тип. Например

explicit-replicate' : ∀ α -> (A : Set α) -> (n : ℕ) -> A -> Vec A n
explicit-replicate' _ _ _ x = replicate x

... because this would result in an invalid use of Setω ...
error : ∀ α -> (A : Set α) -> ℕ -> A -> ℕ
error = comp 4 length explicit-replicate'

Но для Агды это характерно, например, вы не можете применить явное id к себе:

idₑ : ∀ α -> (A : Set α) -> A -> A
idₑ _ _ x = x

-- ... because this would result in an invalid use of Setω ...
error = idₑ _ _ idₑ

Код.

Другие вопросы по тегам