Jaka jest różnica między ADT, GADT i typami indukcyjnymi?

21

Może ktoś będzie w stanie wyjaśnić różnicę między:

  • Algebraiczne typy danych (które dość dobrze znam)
  • Uogólnione algebraiczne typy danych (co czyni je uogólnionymi?)
  • Rodzaje indukcyjne (np. Coq)

(Szczególnie typy indukcyjne.) Dziękuję.

ninjagecko
źródło

Odpowiedzi:

21

Algebraiczne typy danych umożliwiają rekurencyjne definiowanie typów. Konkretnie, załóżmy, że mamy typ danych

rezatzaljast=N.jal|doonsofaN.×ljast

Oznacza to, że jest najmniejszym zestawem generowanym przez operatorów i . Możemy to sformalizować, definiując operatorN i l C o n s F ( X )ljastN.jaldoonsfa(X)

fa(X)=={N.jal}{doons(n,x)|nN.xX}

a następnie zdefiniowanie jakoljast

ljast=jaN.faja()

Uogólnione ADT jest to, co otrzymujemy, kiedy zdefiniować typ operatora rekurencyjnie. Na przykład możemy zdefiniować następujący konstruktor typu:

bushza=L.mizafaofaza|N.mistofabush(za×za)

Ten typ oznacza, że element jest krotką s o długości jakiegoś , ponieważ za każdym razem gdy w konstruktor typ argumentu jest połączony ze sobą . Możemy więc zdefiniować operatora, który chcemy przyjąć jako punkt stały:a 2 n n N e s tbushzaza2)nnN.mist

fa(R)=λX.{L.mizafa(x)|xX}{N.mist(v)|vR(X)}

Typ indukcyjny w Coq jest zasadniczo GADT, gdzie indeksy operatora typu nie są ograniczone do innych typów (jak na przykład Haskell), ale mogą być również indeksowane według wartości teorii typów. Dzięki temu możesz podać typy list indeksowanych według długości i tak dalej.

Neel Krishnaswami
źródło
1
Dziękuję Ci. Czy nie oznacza to jednak, że „typ indukcyjny” jest całkowicie synonimem „typu zależnego”?
ninjagecko
4
@Neel: Nigdy nie widziałem typów takich jak bushGADT. Widziałem je nazywane typami zagnieżdżonymi lub nieregularnymi.
jbapple,
3
Typy zagnieżdżone są szczególnym przypadkiem GADT. Krytyczną cechą GADT jest po prostu to, że jest to definicja rekurencyjna na wyższym poziomie. (Zmiany w rhs to w zasadzie cukier syntaktyczny służący do dodania równości typu jako elementu konstruktora.)
Neel Krishnaswami
4
@ninjagecko: „Typy indukcyjne” to typy, które mają semantykę jako najmniej ustalony punkt konstruktora. Nie wszystkie typy można opisać w ten sposób (funkcje nie mogą, podobnie jak typy nieskończone, takie jak strumienie). Typy zależne opisują typy, które pozwalają na występowanie w nich warunków programu (to znaczy typy mogą „zależeć od” warunków). Ponieważ Coq jest teorią typów zależnych, typy indukcyjne, które pozwala zdefiniować, są również zależne. Ale teorie typów niezależnych mogą również obsługiwać typy indukcyjne, a te typy indukcyjne nie będą zależne.
Neel Krishnaswami,
2
@NeelKrishnaswami: Czy byłbyś tak uprzejmy, aby wyjaśnić swoją odpowiedź, wymieniając „kilka pierwszych najmniejszych” elementów typów bush a? W tym przykładzie jest to Nest Leaf(a) Leaf(a) Leaf(a) Leaf(a), czy Nest ((Nest Leaf(a) Leaf(a)) (Nest Leaf(a) Leaf(a)))jako jeden z przykładów zestawu?
ninjagecko
19

Rozważ algebraiczne typy danych, takie jak:

data List a = Nil | Cons a (List a)

Rodzaje powrotne każdego konstruktora w typ danych są takie same: Nili Consobaj powrót List a. Jeśli pozwolimy konstruktorom zwracać różne typy, mamy GADT :

data Empty -- this is an empty data declaration; Empty has no constructors
data NonEmpty

data NullableList a t where
    Vacant :: NullableList a Empty
    Occupied :: a -> NullableList a b -> NullableList a NonEmpty

Occupiedma typ a -> NullableList a b -> NullableList a NonEmpty, a Consma typ a -> List a -> List a. Należy zauważyć, że NonEmptyjest to typ, a nie termin. Inny przykład:

data Zero
data Succ n

data SizedList a t where
    Alone :: SizedList a Zero
    WithFriends :: a -> SizedList a n -> SizedList a (Succ n)

Typy indukcyjne w językach programowania, które mają typy zależne, pozwalają typom zwracanym przez konstruktory zależeć od wartości (nie tylko typów) argumentów.

Inductive Parity := Even | Odd.

Definition flipParity (x:Parity) : Parity :=
  match x with
    | Even => Odd
    | Odd => Even
  end.

Fixpoint getParity (x:nat) : Parity :=
  match x with
    | 0 => Even
    | S n => flipParity (getParity n)
  end.

(*
A ParityNatList (Some P) is a list in which each member
is a natural number with parity P.
*)

Inductive ParityNatList : option Parity -> Type :=
  Nil : forall P, ParityNatList P
| Cons : forall (x:nat) (P:option Parity), 
  ParityNatList P -> ParityNatList 
  (match P, getParity x with
     | Some Even, Even => Some Even
     | Some Odd, Odd => Some Odd
     | _, _ => None
   end).

Uwaga dodatkowa: GHC ma mechanizm traktowania konstruktorów wartości jako konstruktorów typów . Nie jest to to samo co zależne typy indukcyjne, które posiada Coq, ale nieco zmniejsza obciążenie składniowe GADT i może prowadzić do lepszych komunikatów o błędach.

jbapple
źródło
Dziękuję Ci. „Typy indukcyjne w językach programowania, które mają typy zależne” Jak zatem wyglądałby typ indukcyjny w języku bez typów zależnych i czy można mieć typy nieindukcyjne (ale podobne do GADT)?
ninjagecko