Sự khác biệt giữa các ADT, GADT và các loại quy nạp là gì?


21

Bất cứ ai cũng có thể giải thích sự khác biệt giữa:

  • Các kiểu dữ liệu đại số (mà tôi khá quen thuộc)
  • Các kiểu dữ liệu đại số tổng quát (điều gì làm cho chúng tổng quát?)
  • Các loại quy nạp (ví dụ Coq)

(Đặc biệt là các loại quy nạp.) Cảm ơn bạn.

Câu trả lời:


21

Các kiểu dữ liệu đại số cho phép bạn xác định các kiểu đệ quy. Cụ thể, giả sử chúng ta có kiểu dữ liệu

datalist=Nil|ConsofN×list

Điều này có nghĩa là là tập nhỏ nhất được tạo bởi các toán tử N i lC o n s . Chúng ta có thể chính thức hóa điều này bằng cách định nghĩa toán tử F ( X )listNilConsF(X)

F(X)=={Nil}{Cons(n,x)|nNxX}

và sau đó xác định list

list=iNFi()

Một ADT tổng quát là những gì chúng ta nhận được khi định nghĩa một toán tử kiểu đệ quy. Ví dụ: chúng ta có thể định nghĩa hàm tạo kiểu sau:

busha=Leafofa|Nestofbush(a×a)

Loại này có nghĩa là một yếu tố của là một tuple độ dài 2 n đối với một số n , vì mỗi lần chúng ta đi vào N e s tbushaa2nnNest , đối số kiểu được ghép với chính nó. Vì vậy, chúng ta có thể định nghĩa toán tử mà chúng ta muốn lấy một điểm cố định là:

F(R)=λX.{Leaf(x)|xX}{Nest(v)|vR(X)}

Một loại quy nạp trong Coq thực chất là một GADT, trong đó các chỉ mục của toán tử loại không bị hạn chế đối với các loại khác (ví dụ như trong Haskell), nhưng cũng có thể được lập chỉ mục bởi các giá trị của lý thuyết loại. Điều này cho phép bạn đưa ra các loại cho danh sách được lập chỉ mục độ dài, v.v.


1
Cảm ơn bạn. Tuy nhiên, điều đó có nghĩa là "loại quy nạp" hoàn toàn đồng nghĩa với "loại phụ thuộc"?
ninjagecko

4
@Neel: Tôi chưa bao giờ thấy các loại như bushđược gọi là GADT. Tôi đã thấy chúng được gọi là loại lồng nhau hoặc không thường xuyên.
jbapple

3
Các loại lồng nhau là một trường hợp đặc biệt của GADT. Tính năng quan trọng của GADT chỉ đơn giản là nó là một định nghĩa đệ quy ở loại cao hơn. (Thay đổi về rhs về cơ bản là đường cú pháp để thêm một đẳng thức như là một thành phần của hàm tạo.)
Neel Krishnaswami

4
@ninjagecko: "Các loại quy nạp" là các loại được đưa ra về ngữ nghĩa như là điểm cố định ít nhất của một hàm tạo. Không phải tất cả các loại có thể được mô tả theo cách này (các chức năng không thể và cũng không thể loại vô hạn như các luồng). Các loại phụ thuộc mô tả các loại cho phép các điều khoản chương trình xảy ra trong chúng (nghĩa là các loại có thể "phụ thuộc" vào các điều khoản). Vì Coq là một lý thuyết loại phụ thuộc, các loại quy nạp mà nó cho phép bạn xác định cũng phụ thuộc. Nhưng các lý thuyết loại không phụ thuộc cũng có thể hỗ trợ các loại quy nạp và các loại quy nạp đó sẽ không phụ thuộc.
Neel Krishnaswami

2
@NeelKrishnaswami: Bạn có tử tế khi làm rõ câu trả lời của mình bằng cách liệt kê các yếu tố "vài lần đầu tiên nhỏ nhất" của các loại bush akhông? Trong ví dụ này, nó là Nest Leaf(a) Leaf(a) Leaf(a) Leaf(a), hay Nest ((Nest Leaf(a) Leaf(a)) (Nest Leaf(a) Leaf(a)))là một ví dụ của tập hợp?
ninjagecko

19

Xem xét các kiểu dữ liệu đại số như:

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

Các kiểu trả về của mỗi hàm tạo trong một kiểu dữ liệu đều giống nhau: NilConscả hai trả về List a. Nếu chúng tôi cho phép các nhà xây dựng trả về các loại khác nhau, chúng tôi có 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

Occupiedcó loại a -> NullableList a b -> NullableList a NonEmpty, trong khi Conscó loại a -> List a -> List a. Điều quan trọng cần lưu ý NonEmptylà một loại, không phải là một thuật ngữ. Một vi dụ khac:

data Zero
data Succ n

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

Các kiểu quy nạp trong các ngôn ngữ lập trình có các kiểu phụ thuộc cho phép các kiểu trả về của các hàm tạo phụ thuộc vào các giá trị (không chỉ các kiểu) của các đối số.

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).

Lưu ý bên lề: GHC có một cơ chế xử lý các hàm tạo giá trị như các hàm tạo kiểu . Điều này không giống với các loại quy nạp phụ thuộc mà Coq có, nhưng nó giảm bớt gánh nặng cú pháp của GADT phần nào và nó có thể dẫn đến các thông báo lỗi tốt hơn.


Cảm ơn bạn. "Các loại quy nạp trong các ngôn ngữ lập trình có các loại phụ thuộc" Sau đó, một loại quy nạp sẽ trông như thế nào trong một ngôn ngữ không có các loại phụ thuộc và bạn có thể có các loại phụ thuộc không quy nạp (nhưng giống như GADT) không?
ninjagecko
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.