5

Utilizzando la codifica Church, è possibile rappresentare qualsiasi tipo di dati algebrico arbitrario senza utilizzare il sistema ADT incorporato. Ad esempio, può essere rappresentato Nat (esempio in Idris) come:È possibile creare una rappresentazione a livello di carattere di ADT generici?

-- Original type 

data Nat : Type where 
    natSucc : Nat -> Nat 
    natZero : Nat 

-- Lambda encoded representation 

Nat : Type 
Nat = (Nat : Type) -> (Nat -> Nat) -> Nat -> Nat 

natSucc : Nat -> Nat 
natSucc pred n succ zero = succ (pred n succ zero) 

natZero : Nat 
natZero n succ zero = zero 

Pair può essere rappresentato come:

-- Original type 
data Pair_ : (a : Type) -> (b : Type) -> Type where 
    mkPair_ : (x:a) -> (y:b) -> Pair_ a b 

-- Lambda encoded representation 

Par : Type -> Type -> Type 
Par a b = (t:Type) -> (a -> b -> t) -> t 

pair : (ta : Type) -> (tb : Type) -> (a:ta) -> (b:tb) -> Par ta tb 
pair ta tb a b k t = t a b 

fst : (ta:Type) -> (tb:Type) -> Par ta tb -> ta 
fst ta tb pair = pair ta (\ a, b => a) 

snd : (ta:Type) -> (tb:Type) -> Par ta tb -> tb 
snd ta tb pair = pair tb (\ a, b => b) 

E così via. Ora, scrivere quei tipi, costruttori, abbinatori è un compito molto meccanico. La mia domanda è: sarebbe possibile rappresentare un ADT come una specifica a livello di tipo, quindi derivare i tipi stessi (cioè, Nat/Par), nonché i costruttori/distruttori automaticamente da quelle specifiche? Allo stesso modo, potremmo usare queste specifiche per derivare i farmaci generici? Esempio:

NAT : ADT 
NAT = ... some type level expression ... 

Nat : Type 
Nat = DeriveType NAT 

natSucc : ConstructorType 0 NAT 
natSucc = Constructor 0 NAT 

natZero : ConstructorType 1 NAT 
natZero = Constructor 1 NAT 

natEq : EqType NAT 
natEq = Eq NAT 

natShow : ShowType NAT 
natShow = Show NAT 

... and so on 
+3

Si noti che, per quel che ne so, queste codifiche ecclesiastiche mancano di eliminazioni dipendenti. Per esempio. se 'Bool = (A: Type) -> A -> A -> A', e' tru, fls' sono definiti di conseguenza, non puoi provare '(b: Bool) -> (b = tru \/b = fls) ', mentre potevi con tipi induttivi. – chi

+2

È possibile farlo per un sistema di tipo molto più espressivo: tipi completi dipendenti + famiglie induttive. Derivato 'Nat' quindi è un eliminatore' Nat = (P: Nat -> Tipo) -> (per tutti n Pn -> P (succ n)) -> P 0 -> forall n. P n'. Ho scritto un [post di blog] (http://effectfully.blogspot.com/2016/06/deriving-eliminators-of-described-data.html) a riguardo. 'EqType' è [derivable] (https://github.com/effectfully/Generic/blob/master/Property/Eq.agda) (un [esempio] (https://github.com/effectfully/Generic/blob /master/Examples/Eq.agda)). Quanti tipi vuoi coprire? Solo i tipi di sistema F e nessun GADT? – user3237465

+0

@chi: in effetti, questa sembra essere una fonte che conferma la tua affermazione: [L'induzione non è derivabile nella teoria del tipo dipendente secondo ordine] (https://scholar.google.com.sg/scholar?cluster=4467817914024141350&hl=it&as_sdt=0 , 5) – Cactus

risposta

3

Per iniziare, ecco qualche codice Idris che rappresenta funtori polinomi:

infix 10 |+| 
infix 10 |*| 

data Functor : Type where 
    Rec : Functor 
    Emb : Type -> Functor 
    (|+|) : Functor -> Functor -> Functor 
    (|*|) : Functor -> Functor -> Functor 

LIST : Type -> Functor 
LIST a = Emb Unit |+| (Emb a |*| Rec) 

TUPLE2 : Type -> Type -> Functor 
TUPLE2 a b = Emb a |*| Emb b 

NAT : Functor 
NAT = Rec |+| Emb Unit 

Ecco un'interpretazione dei dati a base dei loro punti fissi (si veda ad esempio 3.2 in http://www.cse.chalmers.se/~ulfn/papers/afp08/tutorial.pdf per ulteriori dettagli)

adt : Functor -> Type -> Type 
adt Rec t = t 
adt (Emb a) _ = a 
adt (f |+| g) t = Either (adt f t) (adt g t) 
adt (f |*| g) t = (adt f t, adt g t) 

data Mu : (F : Functor) -> Type where 
    Fix : {F : Functor} -> adt F (Mu F) -> Mu F 

ed ecco un'interpretazione basata rappresentazione Chiesa:

Church : Functor -> Type 
Church f = (t : Type) -> go f t t 
    where 
    go : Functor -> Type -> (Type -> Type) 
    go Rec t = \r => t -> r 
    go (Emb a) t = \r => a -> r 
    go (f |+| g) t = \r => go f t r -> go g t r -> r 
    go (f |*| g) t = go f t . go g t 

Quindi possiamo fare ad es.

-- Need the prime ticks because otherwise clashes with Nat, zero, succ from the Prelude... 
Nat' : Type 
Nat' = Mu NAT 

zero' : Nat' 
zero' = Fix (Right()) 

succ' : Nat' -> Nat' 
succ' n = Fix (Left n) 

ma anche

zeroC : Church NAT 
zeroC n succ zero = (zero()) 

succC : Church NAT -> Church NAT 
succC pred n succ zero = succ (pred n succ zero) 
+1

Questo può essere esteso oltre i punti fissi dei funtori polinomiali? per esempio. utilizzando [descrizioni] (https://personal.cis.strath.ac.uk/conor.mcbride/levitation.pdf)? –

+0

Insightful e molto più semplice di quanto pensassi. Secondo, mi piacerebbe che fosse ampliato per spiegare le descrizioni in termini semplici. – MaiaVictor

+1

@ Benjamin Hodgson, ho aggiunto una risposta relativa alle descrizioni. – user3237465

6

descrizioni indicizzate non sono più duro di funtori polinomi. Considerate questa semplice forma di propositional descriptions:

data Desc (I : Set) : Set₁ where 
    ret : I -> Desc I 
    π : (A : Set) -> (A -> Desc I) -> Desc I 
    _⊕_ : Desc I -> Desc I -> Desc I 
    ind : I -> Desc I -> Desc I 

π è come Emb seguita da |*|, ma permette al resto della descrizione dipende da un valore di tipo A. _⊕_ è la stessa cosa di |+|. ind è come Rec seguito da |*|, ma riceve anche l'indice di un sottordine futuro. ret termina una descrizione e specifica l'indice di un termine costruito. Ecco un esempio immediato:

vec : Set -> Desc ℕ 
vec A = ret 0 
     ⊕ π ℕ λ n -> π A λ _ -> ind n $ ret (suc n) 

Il primo costruttore di vec non contiene dati e costruisce un vettore di lunghezza 0, quindi abbiamo messo ret 0. Il secondo costruttore riceve la lunghezza (n) di un subvector, un elemento di tipo A e un subvector e costruisce un vettore di lunghezza suc n.

Costruire punti fissi di descrizioni è simile a quelli di funtori polinomiali troppo

⟦_⟧ : ∀ {I} -> Desc I -> (I -> Set) -> I -> Set 
⟦ ret i ⟧ B j = i ≡ j 
⟦ π A D ⟧ B j = ∃ λ x -> ⟦ D x ⟧ B j 
⟦ D ⊕ E ⟧ B j = ⟦ D ⟧ B j ⊎ ⟦ E ⟧ B j 
⟦ ind i D ⟧ B j = B i × ⟦ D ⟧ B j 

data μ {I} (D : Desc I) j : Set where 
    node : ⟦ D ⟧ (μ D) j -> μ D j 

Vec è semplicemente

Vec : Set -> ℕ -> Set 
Vec A = μ (vec A) 

precedenza era adt Rec t = t, ma ora termini sono indicizzati, quindi t vengono indicizzati troppo (Si chiama B sopra). ind i D riporta l'indice di un sottotema i a cui viene applicato quindi μ D. Pertanto, quando si interpreta il secondo costruttore di vettori, Vec A viene applicato alla lunghezza di un subvettore n (da ind n $ ...), quindi un sottotermino ha tipo Vec A n.

Nel caso finale ret i è necessario che un termine costruito abbia lo stesso indice (i) come previsto (j).

Derivazione eliminatori per tali tipi di dati è leggermente più complicato:

Elim : ∀ {I B} -> (∀ {i} -> B i -> Set) -> (D : Desc I) -> (∀ {j} -> ⟦ D ⟧ B j -> B j) -> Set 
Elim C (ret i) k = C (k refl) 
Elim C (π A D) k = ∀ x -> Elim C (D x) (k ∘ _,_ x) 
Elim C (D ⊕ E) k = Elim C D (k ∘ inj₁) × Elim C E (k ∘ inj₂) 
Elim C (ind i D) k = ∀ {y} -> C y -> Elim C D (k ∘ _,_ y) 

module _ {I} {D₀ : Desc I} (P : ∀ {j} -> μ D₀ j -> Set) (f₀ : Elim P D₀ node) where 
    mutual 
    elimSem : ∀ {j} 
      -> (D : Desc I) {k : ∀ {j} -> ⟦ D ⟧ (μ D₀) j -> μ D₀ j} 
      -> Elim P D k 
      -> (e : ⟦ D ⟧ (μ D₀) j) 
      -> P (k e) 
    elimSem (ret i) z  refl = z 
    elimSem (π A D) f  (x , e) = elimSem (D x) (f x) e 
    elimSem (D ⊕ E) (f , g) (inj₁ x) = elimSem D f x 
    elimSem (D ⊕ E) (f , g) (inj₂ y) = elimSem E g y 
    elimSem (ind i D) f  (d , e) = elimSem D (f (elim d)) e 

    elim : ∀ {j} -> (d : μ D₀ j) -> P d 
    elim (node e) = elimSem D₀ f₀ e 

ho elaborato i dettagli elsewhere.

Può essere utilizzato in questo modo:

elimVec : ∀ {n A} 
     -> (P : ∀ {n} -> Vec A n -> Set) 
     -> (∀ {n} x {xs : Vec A n} -> P xs -> P (x ∷ xs)) 
     -> P [] 
     -> (xs : Vec A n) 
     -> P xs 
elimVec P f z = elim P (z , λ _ -> f) 

Derivazione di uguaglianza decidibile è più prolisso, ma non di più: è solo una questione di che richiede che ogni Set che π riceve ha uguaglianza decidibile. Se tutto il contenuto non ricorsivo del tuo tipo di dati ha uguaglianza decidibile, allora il tuo tipo di dati lo ha pure.

The code.