Общее программирование с помощью эффектов

В библиотеке эффектов Idris Effects есть представлен как

||| This type is parameterised by:
||| + The return type of the computation.
||| + The input resource.
||| + The computation to run on the resource given the return value.
Effect : Type
Effect = (x : Type) -> Type -> (x -> Type) -> Type

Если мы позволим ресурсам быть значениями и поменять местами первые два аргумента, мы получим (остальной код находится в Agda)

Effect : Set -> Set
Effect R = R -> (A : Set) -> (A -> R) -> Set

Наличие некоторого базового механизма тип-контекст-членство

data Type : Set where
  nat : Type
  _⇒_ : Type -> Type -> Type

data Con : Set where
  ε   : Con
  _▻_ : Con -> Type -> Con

data _∈_ σ : Con -> Set where
  vz  : ∀ {Γ}   -> σ ∈ Γ ▻ σ
  vs_ : ∀ {Γ τ} -> σ ∈ Γ     -> σ ∈ Γ ▻ τ

мы можем кодировать конструкторы лямбда-терминов следующим образом:

app-arg : Bool -> Type -> Type -> Type
app-arg b σ τ = if b then σ ⇒ τ else σ

data TermE : Effect (Con × Type) where
  Var   : ∀ {Γ σ  } -> σ ∈ Γ -> TermE (Γ , σ     )  ⊥     λ()
  Lam   : ∀ {Γ σ τ} ->          TermE (Γ , σ ⇒ τ )  ⊤    (λ _ -> Γ ▻ σ , τ            )
  App   : ∀ {Γ σ τ} ->          TermE (Γ , τ     )  Bool (λ b -> Γ     , app-arg b σ τ)

В TermE i r i′ i - это выходной индекс (например, лямбда-абстракции (Lam) создают типы функций (σ ⇒ τ) (для простоты описания я проигнорирую, что индексы также содержат контексты помимо типов)), r представляет собой количество индуктивных позиций (Var не ' t () принимает любое TermE, Lam получает один (), App получает два (Bool) - функцию и ее аргумент) и i′ вычисляет индекс в каждой индуктивной позиции (например, индекс в первой индуктивной позиции App равен σ ⇒ τ а индекс во втором - σ, т.е. мы можем применить функцию к значению, только если тип первого аргумента функции равен типу значения).

Чтобы создать настоящий лямбда-термин, мы должны связать узел, используя что-то вроде W тип данных. Вот определение:

data Wer {R} (Ψ : Effect R) : Effect R where
  call : ∀ {r A r′ B r′′} -> Ψ r A r′ -> (∀ x -> Wer Ψ (r′ x) B r′′) -> Wer Ψ r B r′′

Это проиндексированный вариант монады Олега Киселева Freer (снова оказывает влияние), но без return. Используя это, мы можем восстановить обычные конструкторы:

_<∨>_ : ∀ {B : Bool -> Set} -> B true -> B false -> ∀ b -> B b
(x <∨> y) true  = x
(x <∨> y) false = y

_⊢_ : Con -> Type -> Set
Γ ⊢ σ = Wer TermE (Γ , σ) ⊥ λ()

var : ∀ {Γ σ} -> σ ∈ Γ -> Γ ⊢ σ
var v = call (Var v) λ()

ƛ_ : ∀ {Γ σ τ} -> Γ ▻ σ ⊢ τ -> Γ ⊢ σ ⇒ τ
ƛ b = call Lam (const b)

_·_ : ∀ {Γ σ τ} -> Γ ⊢ σ ⇒ τ -> Γ ⊢ σ -> Γ ⊢ τ
f · x = call App (f <∨> x)

Вся кодировка очень похожа на соответствующую кодировку в условия индексированных контейнеров: Effect соответствует IContainer, а Wer соответствует ITree (тип Петерссон-Синек Деревья). Однако приведенная выше кодировка кажется мне проще, потому что вам не нужно думать о вещах, которые нужно преобразовать в формы, чтобы иметь возможность восстанавливать индексы в индуктивных позициях. Вместо этого у вас все в одном месте, и процесс кодирования действительно прост.

Так что я здесь делаю? Есть ли какое-то реальное отношение к подходу с индексированными контейнерами (помимо того факта, что эта кодировка имеет то же самое проблемы с расширением)? Можем ли мы таким образом сделать что-нибудь полезное? Одна естественная мысль состоит в том, чтобы построить эффективное лямбда-исчисление, поскольку мы можем свободно смешивать лямбда-члены с эффектами, поскольку лямбда-член сам по себе является просто эффектом, но это внешний эффект, и нам либо нужно, чтобы другие эффекты также были внешними (что означает, что мы не можем сказать что-то вроде tell (var vz), потому что var vz не значение - это вычисление) или нам нужно как-то усвоить этот эффект и весь механизм эффектов (что означает, что я не знаю что).

Используемый код.


person user3237465    schedule 22.01.2016    source источник
comment
Возможно, вам повезет больше, если вы спросите в сабреддите / r / haskell. Там есть хорошее сочетание программистов Agda и энтузиастов Freer.   -  person hao    schedule 15.04.2016
comment
@haoformayor, было сообщение в / r / зависимых_типах / subreddit. Однако никаких ответов. Существуют кодировки (например), которые не имеют расширяемости проблемы, поэтому такое кодирование эффектов, вероятно, не очень полезно.   -  person user3237465    schedule 15.04.2016
comment
это хорошо. Я думаю, что сабреддит haskell обычно получает больше трафика, и они не будут возражать против репоста. плюс это отличный вопрос   -  person hao    schedule 15.04.2016
comment
@haoformayor, ОК, тогда.   -  person user3237465    schedule 15.04.2016


Ответы (1)


Интересная работа! Я не очень разбираюсь в эффектах, и у меня есть только базовое представление об индексированных контейнерах, но я занимаюсь общим программированием, так что вот мой взгляд на это.

Тип TermE : Con × Type → (A : Set) → (A → Con × Type) → Set напоминает мне тип описаний, используемых для формализации индексированной индукционной рекурсии в [1]. Во второй главе этой статьи говорится, что между Set/I = (A : Set) × (A → I) и I → Set существует эквивалентность. Это означает, что тип TermE эквивалентен Con × Type → (Con × Type → Set) → Set или (Con × Type → Set) → Con × Type → Set. Последний является индексированным функтором, который используется в стиле полиномиального функтора («сумма произведений») общего программирования, например, в [2] и [3]. Если вы не видели его раньше, он выглядит примерно так:

data Desc (I : Set) : Set1 where
  `Σ : (S : Set) → (S → Desc I) → Desc I
  `var : I → Desc I → Desc I
  `ι : I → Desc I

⟦_⟧ : ∀{I} → Desc I → (I → Set) → I → Set
⟦ `Σ S x ⟧ X o = Σ S (λ s → ⟦ x s ⟧ X o)
⟦ `var i xs ⟧ X o = X i × ⟦ xs ⟧ X o
⟦ `ι o′ ⟧ X o = o ≡ o′

data μ {I : Set} (D : Desc I) : I → Set where
  ⟨_⟩ : {o : I} → ⟦ D ⟧ (μ D) o → μ D o

natDesc : Desc ⊤
natDesc = `Σ Bool (λ { false → `ι tt ; true → `var tt (`ι tt) })
nat-example : μ natDesc tt
nat-example = ⟨ true , ⟨ true , ⟨ false , refl ⟩ , refl ⟩ , refl ⟩
finDesc : Desc Nat
finDesc = `Σ Bool (λ { false → `Σ Nat (λ n → `ι (suc n))
                     ; true → `Σ Nat (λ n → `var n (`ι (suc n)))
                     })
fin-example : μ finDesc 5
fin-example = ⟨ true , 4 , ⟨ true , 3 , ⟨ false , 2 , refl ⟩ , refl ⟩ , refl ⟩

Таким образом, фиксированная точка μ напрямую соответствует вашему Wer типу данных, а интерпретированные описания (с использованием ⟦_⟧) соответствуют вашему TermE. Я предполагаю, что часть литературы по этой теме будет для вас актуальной. Я не помню, эквивалентны ли индексированные контейнеры и индексированные функторы, но они определенно связаны. Я не совсем понимаю ваше замечание о tell (var vz), но может ли это быть связано с интернализацией фиксированных точек в такого рода описаниях? В таком случае, возможно, [3] сможет вам в этом помочь.

person Yorick Sijsling    schedule 18.04.2016
comment
Изоморфизм между Set/I и I -> Set (которые оба кодируют понятие подмножества), кажется, является ключевым. Похоже, если мы заменим Set/I на I -> Set в Effect, мы получим именно абстрактный тип индексированных функторов (в отличие от кодирования первого порядка в вашем [3]). Как ни странно, IWer (который сейчас делает внешние фиксированные точки) даже больше Freer, чем Wer. Таким образом, существует очень тесное соответствие между эффектами и индексированными функторами. - person user3237465; 19.04.2016
comment
Теперь вы можете определить элиминаторы для типов данных, закодированных с помощью индексированных функторов первого порядка? В [3] у них есть только cata, что довольно неудовлетворительно в параметрах с зависимой типизацией. Я знаком с пропозициональными описаниями вы упомянули, и похоже, что они тоже способ упорядочить индексированные функторы и, следовательно, эффекты. Но более подходящий способ, так как они дают вам элиминаторы. - person user3237465; 19.04.2016