Bảo vệ của hoàng tử xuất hiện tiêu cực trong định nghĩa của các loại quy nạp, luôn luôn xấu?


11

Tôi biết làm thế nào một số sự cố tiêu cực có thể là xấu:

data False

data Bad a = C (Bad a -> a)

selfApp :: Bad a -> a
selfApp (x@(C x')) = x' x

yc :: (a -> a) -> a
yc f = selfApp $ C (\x -> f (selfApp x))

false :: False
false = yc id

Tuy nhiên, tôi không chắc liệu:

  • tất cả các loại quy nạp có sự cố tiêu cực có thể biến thành sai;

  • nếu vậy, có một cách cơ học đã biết để làm như vậy;

Ví dụ, tôi đã chiến đấu để làm cho loại này đi sai:

type Not a = a -> False

data Bad2 a = C2 (Bad2 (Not a) -> a)

Bất kỳ con trỏ đến văn học về chủ đề này sẽ được đánh giá cao.


1
Đây có phải là Coq không? Haskell? Lý thuyết kiểu giả? Bạn có ý nghĩa gì khi "đi sai"?
Dave Clarke

@DaveClarke Xin lỗi, mã là Haskell, nhưng mối quan tâm nhiều hơn về các ngôn ngữ như Coq hoặc Agda nơi các sự kiện tiêu cực bị cấm. Bằng cách "đi sai", tôi có nghĩa là có thể viết một thuật ngữ phân kỳ, do đó có thể cư trú Sai như tôi đã làm trong ví dụ của mình trong Haskell.
Ptival

Câu trả lời:


10

Lý do cấm các sự cố tiêu cực có thể được hiểu bằng cách tương tự với định lý Knaster-Tarski. Định lý này nói rằng

nếu là một mạng hoàn chỉnh và là một hàm đơn điệu trên , thì tập hợp các điểm cố định của cũng là một mạng hoàn chỉnh. Cụ thể, có một điểm cố định ít nhất và một điểm cố định lớn nhất .f : L L L f μ f ν eLf:LLLfμfνf

Trong lý thuyết mô hình truyền thống, các mạng có thể được xem như các mệnh đề và quan hệ thứ tự có thể được hiểu là sự đòi hỏi (nghĩa là sự thật của được kéo theo sự thật của ).p q q pLpqqp

Khi chúng ta chuyển từ lý thuyết mô hình sang lý thuyết bằng chứng, mạng tổng quát thành các loại. Loại có thể được coi là đối tượng của một thể loại , và một bản đồ đại diện cho một bằng chứng rằng có thể được bắt nguồn từ . e : P Q Q QCe:PQQQ

Khi chúng tôi cố gắng diễn giải các loại được xác định bởi các phương trình đệ quy, ee, , điều rõ ràng cần làm là tìm kiếm một khái quát của định lý Knaster-Tarski. Vì vậy, thay vì hàm đơn điệu trên mạng, chúng ta biết muốn có functor , gửi các đối tượng đến các đối tượng, nhưng khái quát điều kiện đơn điệu sao cho mọi bản đồ lấy bản đồ (với các điều kiện kết hợp mà gửi danh tính đến danh tính và bảo toàn các chế phẩm sao cho ).F : CC e : P Q F ( e ) : F ( P ) F ( Q ) F F ( g f ) = F ( g ) F ( f )N=μα.1+α F:CCe:PQF(e):F(P)F(Q)FF(gf)=F(g)F(f)

Vì vậy, nếu bạn muốn một kiểu dữ liệu quy nạp , bạn cũng cần cung cấp một hành động functorial về các điều khoản cho toán tử loại để đảm bảo rằng điểm cố định bạn muốn tồn tại. Điều kiện tích cực nghiêm ngặt trong Agda và Coq là một điều kiện cú pháp ngụ ý ràng buộc ngữ nghĩa này . Nói một cách lỏng lẻo, nó nói rằng nếu bạn xây dựng một toán tử loại từ tổng và sản phẩm, thì bạn luôn có thể nấu các hành động functorial, và vì vậy bất kỳ loại nào được hình thành theo cách này nên có một điểm cố định.Fμα.F(α)F

Trong các ngôn ngữ được gõ phụ thuộc, bạn cũng có các loại được lập chỉ mục và tham số hóa, vì vậy nhiệm vụ thực sự của bạn phức tạp hơn. Bob Atkey (người đã viết blog về điều này ở đâyở đây ) nói với tôi rằng một nơi tốt để tìm kiếm câu chuyện là:

Như Andrej lưu ý, về cơ bản việc có xảy ra tiêu cực hay không là tốt hay không phụ thuộc vào mô hình lý thuyết loại của bạn. Về cơ bản, khi bạn có một định nghĩa đệ quy, bạn đang tìm kiếm một điểm cố định và có rất nhiều định lý điểm cố định trong toán học.

Một cái mà cá nhân tôi đã sử dụng rất nhiều là định lý điểm cố định của Banach, nói rằng nếu bạn có một hàm hợp đồng nghiêm ngặt trên một không gian số liệu, thì nó có một điểm cố định duy nhất. Ý tưởng này đã được đưa vào ngữ nghĩa bởi (IIRC) Maurice Nivat, và được nghiên cứu rộng rãi bởi Mỹ và Rutten, và gần đây đã được Birkedal và các cộng tác viên của ông kết nối với một kỹ thuật hoạt động phổ biến được gọi là "lập chỉ mục bước".

Điều này dẫn đến các lý thuyết loại trong đó các trường hợp tiêu cực trong các kiểu đệ quy được cho phép, nhưng chỉ khi các sự kiện tiêu cực xảy ra trong một hàm tạo kiểu "bảo vệ" đặc biệt. Ý tưởng này được đưa ra bởi Hiroshi Nakano và mối liên hệ với định lý của Banach được thực hiện bởi cả tôi và Nick Benton, cũng như Lars Birkedal và các đồng tác giả.


7

Đôi khi bạn có thể giải các phương trình đệ quy "bằng may mắn".

Tôi đoán bạn muốn thực hiện điều này theo bộ (trái ngược với một số loại lý thuyết miền) Nếu chúng tôi mở ra định nghĩa của bạn và viết phương trình trực tiếp mà không có chú thích Haskell, chúng tôi nhận được Chúng ta hãy xem xét hai trường hợp:

A(A)A.
  1. Nếu có người ở, nghĩa là, nó có chứa một cái gì đó, thìMột Một Một 1. 1AA , do đó phương trình giảm xuống Và thực tế, tập đơn giải phương trình.

    AA1.
    1
  2. Nếu ( ) 1 A trống thì chúng ta sẽ nhận được .()1

Kết luận: có hai giải pháp, loại trống (mà bạn gọi False) và loại đơn vị ().

A(A2)2,
data Cow a = Moo ((a -> Bool) -> Bool)

A22AA22A

N22N.
2N22NInteger(Integer -> Bool) -> Bool

3

Thật khó để thêm bất cứ điều gì vào lời giải thích của Andrej hoặc Neel, nhưng tôi sẽ cho nó một phát súng. Tôi sẽ cố gắng giải quyết quan điểm cú pháp, thay vì cố gắng khám phá ngữ nghĩa cơ bản, bởi vì lời giải thích là cơ bản hơn và tôi đưa ra một câu trả lời thẳng thắn hơn cho câu hỏi của bạn.

λ -calculus chứ không phải là hệ thống phức tạp hơn cơ bản Haskell. Tôi đặc biệt tin rằng sự hiện diện của các biến loại có thể khiến bạn bối rối ở một mức độ nhất định.

Tài liệu tham khảo quan trọng như sau:

Mendler, N. (1991). Các loại quy nạp và các ràng buộc loại trong phép tính lambda bậc hai. Tôi không tìm thấy một tài liệu tham khảo trực tuyến Tôi sợ. Tuy nhiên, các tuyên bố và bằng chứng có thể được tìm thấy trong luận án tiến sĩ của Nax (một bài đọc rất được khuyến khích!).

Bad

Bad=BadA

A

λx:Bad.x x:BadA

và vì thế

(λx:Bad.x x) (λx:Bad.x x):A

Bad=F(Bad)
F(X)XF(X) (trang 39-40 của luận án).

Tất nhiên bạn đang làm việc không phải với các kiểu được xác định theo phương trình mà với các hàm tạo , tức là bạn có

data Bad = Pack (Bad -> A)

hơn là bình đẳng nghiêm ngặt. Tuy nhiên bạn có thể định nghĩa

unpack :: Bad -> (Bad -> A)
unpack (Pack f) = f

đủ để kết quả này tiếp tục được giữ:

 (\x:Bad -> unpack x x) (Pack (\x:Bad -> unpack x x))

A


Trong ví dụ thứ hai của bạn, mọi thứ phức tạp hơn một chút, vì bạn có đôi khi dọc theo dòng

Bad=BadA

BadBadBad aBad (Not a)

type Not a = a -> False

với

data Not a = Not a

Sẽ dễ dàng giải quyết nếu Haskell cho phép định nghĩa loại như vậy:

type Acc = Not Acc

Trong trường hợp này, bạn có thể xây dựng một tổ hợp vòng lặp theo cách chính xác như trước đây. Tôi nghi ngờ bạn có thể thực hiện một công trình tương tự (nhưng phức tạp hơn) bằng cách sử dụng

data Acc = D (Not Acc)

Vấn đề ở đây là xây dựng một đẳng cấu

Bad Acc <-> Bad (Not Acc)

bạn phải đối phó với phương sai hỗn hợp.

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.