Phép tương đương logic tổ hợp của lý thuyết loại trực giác là gì?


87

Gần đây tôi đã hoàn thành một khóa học đại học có sử dụng Haskell và Agda (một ngôn ngữ lập trình hàm được đánh máy phụ thuộc) và đang tự hỏi liệu có thể thay thế phép tính lambda trong các phép tính này bằng logic tổ hợp hay không. Với Haskell, điều này có vẻ khả thi bằng cách sử dụng tổ hợp S và K, do đó làm cho nó trở nên đơn giản. Tôi đã tự hỏi tương đương với Agda là gì. Tức là, người ta có thể tạo một ngôn ngữ lập trình hàm được định kiểu phụ thuộc tương đương với Agda mà không cần sử dụng bất kỳ biến nào không?

Ngoài ra, có thể bằng cách nào đó thay thế định lượng bằng các tổ hợp không? Tôi không biết đây có phải là một sự trùng hợp ngẫu nhiên hay không nhưng việc định lượng phổ quát chẳng hạn làm cho một chữ ký kiểu giống như một biểu thức lambda. Có cách nào để loại bỏ định lượng phổ quát khỏi chữ ký kiểu mà không làm thay đổi ý nghĩa của nó không? Ví dụ:

forall a : Int -> a < 0 -> a + a < a

Có thể diễn đạt điều tương tự mà không cần sử dụng forall không?


21
Bắt đầu bằng cách tìm ra các kiểu phụ thuộc nhất có thể cho K (dễ) và S (khá nhiều lông). Sẽ rất thú vị nếu bạn đưa vào các hằng số cho Set và Pi, sau đó cố gắng xây dựng lại hệ thống Set: Set cơ bản (không nhất quán). Tôi muốn nghĩ xa hơn, nhưng tôi có một chiếc máy bay để bắt.
pigworker

Câu trả lời:


52

Vì vậy, tôi đã nghĩ về nó nhiều hơn một chút và đạt được một số tiến bộ. Đây là bước đầu tiên trong việc mã hóa Set : Sethệ thống đơn giản (nhưng không nhất quán) của Martin-Löf theo phong cách tổ hợp. Đây không phải là cách tốt để kết thúc, nhưng đây là cách dễ dàng nhất để bắt đầu. Cú pháp của lý thuyết kiểu này chỉ là phép tính lambda với chú thích kiểu, kiểu Pi và một Tập hợp vũ trụ.

Lý thuyết loại mục tiêu

Vì lợi ích của sự hoàn chỉnh, tôi sẽ trình bày các quy tắc. Tính hợp lệ của ngữ cảnh chỉ nói rằng bạn có thể xây dựng các ngữ cảnh từ trống bằng cách kết nối các biến mới đang cư trú Set.

                     G |- valid   G |- S : Set
--------------     ----------------------------- x fresh for G
  . |- valid         G, x:S |- valid

Và bây giờ chúng ta có thể nói cách tổng hợp các loại cho các thuật ngữ trong bất kỳ ngữ cảnh nhất định nào và cách thay đổi loại của một thứ gì đó theo hành vi tính toán của các thuật ngữ mà nó chứa.

  G |- valid             G |- S : Set   G |- T : Pi S \ x:S -> Set
------------------     ---------------------------------------------
  G |- Set : Set         G |- Pi S T : Set

  G |- S : Set   G, x:S |- t : T x         G |- f : Pi S T   G |- s : S
------------------------------------     --------------------------------
  G |- \ x:S -> t : Pi S T                 G |- f s : T s

  G |- valid                  G |- s : S   G |- T : Set
-------------- x:S in G     ----------------------------- S ={beta} T
  G |- x : S                  G |- s : T

Trong một biến thể nhỏ so với bản gốc, tôi đã đặt lambda trở thành toán tử ràng buộc duy nhất, vì vậy đối số thứ hai của Pi phải là một hàm tính toán theo cách kiểu trả về phụ thuộc vào đầu vào. Theo quy ước (ví dụ: trong Agda, nhưng đáng buồn là không phải trong Haskell), phạm vi của lambda mở rộng sang bên phải càng nhiều càng tốt, vì vậy bạn thường có thể để các phần trừu tượng không được đánh dấu khi chúng là đối số cuối cùng của toán tử bậc cao hơn: bạn có thể thấy tôi đã làm điều đó với Pi. Loại Agda của bạn (x : S) -> Ttrở thành Pi S \ x:S -> T.

(Ký hiệu hóa . Nhập chú thích trên lambda là cần thiết nếu bạn muốn có thể tổng hợp loại trừu tượng. Nếu bạn chuyển sang kiểm tra loại dưới dạng toán hạng mô đun của mình, bạn vẫn cần chú thích để kiểm tra một phiên bản beta-redx (\ x -> t) s, vì bạn không có cách nào để đoán các loại của các bộ phận từ tổng thể. Tôi khuyên các nhà thiết kế hiện đại nên kiểm tra các loại và loại trừ các lỗi beta khỏi chính cú pháp.)

( Digression . Hệ thống này là không phù hợp như Set:Setcho phép mã hóa của một loạt các "nghịch lý kẻ nói dối". Khi Martin-LOF đề xuất lý thuyết này, Girard gửi cho ông một mã hóa của nó trong mâu thuẫn của chính mình hệ thống U. Nghịch lý tiếp theo do Hurkens là xây dựng độc hại gọn gàng nhất mà chúng tôi biết.)

Cú pháp tổ hợp và chuẩn hóa

Nhưng dù sao, chúng tôi có hai ký hiệu phụ, Pi và Set, vì vậy chúng tôi có thể quản lý một bản dịch tổ hợp với S, K và hai ký hiệu phụ: Tôi chọn U cho vũ trụ và P cho sản phẩm.

Bây giờ chúng ta có thể xác định cú pháp tổ hợp không định kiểu (với các biến miễn phí):

data SKUP = S | K | U | P deriving (Show, Eq)

data Unty a
  = C SKUP
  | Unty a :. Unty a
  | V a
  deriving (Functor, Eq)
infixl 4 :.

Lưu ý rằng tôi đã bao gồm các phương tiện để bao gồm các biến miễn phí được đại diện bởi kiểu atrong cú pháp này. Ngoài việc là một phản xạ từ phía tôi (mọi cú pháp xứng đáng với tên gọi là một đơn nguyên miễn phí với returncác biến nhúng và >>=thay thế hoàn thiện), sẽ rất hữu ích khi đại diện cho các giai đoạn trung gian trong quá trình chuyển đổi các thuật ngữ có ràng buộc với dạng tổ hợp của chúng.

Đây là chuẩn hóa:

norm :: Unty a -> Unty a
norm (f :. a)  = norm f $. a
norm c         = c

($.) :: Unty a -> Unty a -> Unty a        -- requires first arg in normal form
C S :. f :. a $. g  = f $. g $. (a :. g)  -- S f a g = f g (a g)   share environment
C K :. a $. g       = a                   -- K a g = a             drop environment
n $. g              = n :. norm g         -- guarantees output in normal form
infixl 4 $.

(Một bài tập cho người đọc là xác định một kiểu cho chính xác các dạng thông thường và làm sắc nét các kiểu của các phép toán này.)

Lý thuyết kiểu biểu diễn

Bây giờ chúng ta có thể xác định một cú pháp cho lý thuyết kiểu của chúng ta.

data Tm a
  = Var a
  | Lam (Tm a) (Tm (Su a))    -- Lam is the only place where binding happens
  | Tm a :$ Tm a
  | Pi (Tm a) (Tm a)          -- the second arg of Pi is a function computing a Set
  | Set
  deriving (Show, Functor)
infixl 4 :$

data Ze
magic :: Ze -> a
magic x = x `seq` error "Tragic!"

data Su a = Ze | Su a deriving (Show, Functor, Eq)

Tôi sử dụng biểu diễn chỉ số de Bruijn theo cách Bellegarde và Hook (được phổ biến bởi Bird và Paterson). Kiểu Su acó nhiều phần tử hơn a, và chúng tôi sử dụng nó như kiểu của các biến tự do trong một chất kết dính, với Zelàm biến mới được ràng buộc và Su xlà đại diện dịch chuyển của biến tự do cũ x.

Dịch thuật ngữ sang người kết hợp

Và sau khi hoàn thành, chúng tôi có được bản dịch thông thường, dựa trên sự trừu tượng hóa dấu ngoặc .

tm :: Tm a -> Unty a
tm (Var a)    = V a
tm (Lam _ b)  = bra (tm b)
tm (f :$ a)   = tm f :. tm a
tm (Pi a b)   = C P :. tm a :. tm b
tm Set        = C U

bra :: Unty (Su a) -> Unty a               -- binds a variable, building a function
bra (V Ze)      = C S :. C K :. C K        -- the variable itself yields the identity
bra (V (Su x))  = C K :. V x               -- free variables become constants
bra (C c)       = C K :. C c               -- combinators become constant
bra (f :. a)    = C S :. bra f :. bra a    -- S is exactly lifted application

Gõ Combinators

Bản dịch cho thấy cách chúng ta sử dụng các tổ hợp, cung cấp cho chúng ta manh mối về loại của chúng nên là gì. UPchỉ là các hàm tạo được thiết lập, vì vậy, khi viết các kiểu chưa được dịch và cho phép "ký hiệu Agda" cho Pi, chúng ta nên có

U : Set
P : (A : Set) -> (B : (a : A) -> Set) -> Set

Bộ Ktổ hợp được sử dụng để nâng một giá trị của một số loại Alên một hàm không đổi so với một số loại khác G.

  G : Set   A : Set
-------------------------------
  K : (a : A) -> (g : G) -> A

Bộ Skết hợp được sử dụng để nâng các ứng dụng trên một loại, mà tất cả các bộ phận có thể phụ thuộc vào.

  G : Set
  A : (g : G) -> Set
  B : (g : G) -> (a : A g) -> Set
----------------------------------------------------
  S : (f : (g : G) ->    (a : A g) -> B g a   ) ->
      (a : (g : G) ->    A g                  ) ->
           (g : G) ->    B g (a g)

Nếu bạn nhìn vào kiểu S, bạn sẽ thấy rằng nó nêu chính xác quy tắc ứng dụng theo ngữ cảnh của lý thuyết kiểu, vì vậy đó là điều làm cho nó phù hợp để phản ánh cấu trúc ứng dụng. Đó là công việc của nó!

Sau đó, chúng tôi chỉ có ứng dụng cho những thứ đã đóng cửa

  f : Pi A B
  a : A
--------------
  f a : B a

Nhưng có một khó khăn. Tôi đã viết các loại tổ hợp trong lý thuyết kiểu thông thường, không phải lý thuyết kiểu tổ hợp. May mắn thay, tôi có một chiếc máy sẽ thực hiện bản dịch.

Hệ thống loại tổ hợp

---------
  U : U

---------------------------------------------------------
  P : PU(S(S(KP)(S(S(KP)(SKK))(S(KK)(KU))))(S(KK)(KU)))

  G : U
  A : U
-----------------------------------------
  K : P[A](S(S(KP)(K[G]))(S(KK)(K[A])))

  G : U
  A : P[G](KU)
  B : P[G](S(S(KP)(S(K[A])(SKK)))(S(KK)(KU)))
--------------------------------------------------------------------------------------
  S : P(P[G](S(S(KP)(S(K[A])(SKK)))(S(S(KS)(S(S(KS)(S(KK)(K[B])))(S(KK)(SKK))))
      (S(S(KS)(KK))(KK)))))(S(S(KP)(S(S(KP)(K[G]))(S(S(KS)(S(KK)(K[A])))
      (S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KP)))(S(KK)(K[G]))))
      (S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(S(KS)(S(KK)(KS)))
      (S(S(KS)(S(KK)(KK)))(S(KK)(K[B])))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))
      (S(KK)(KK))))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(KK)(KK)))
      (S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))(S(KK)(KK)))))))

  M : A   B : U
----------------- A ={norm} B
  M : B

Vì vậy, bạn có nó, trong tất cả vinh quang không thể đọc được của nó: một bài thuyết trình kết hợp của Set:Set!

Vẫn còn một chút vấn đề. Cú pháp của hệ thống không cung cấp cho bạn cách nào để đoán G, ABcác tham số cho Svà tương tự K, chỉ từ các thuật ngữ. Tương ứng, chúng tôi có thể xác minh các dẫn xuất nhập bằng thuật toán, nhưng chúng tôi không thể chỉ đánh máy các cụm từ tổ hợp như chúng tôi có thể làm với hệ thống ban đầu. Những gì có thể hoạt động là yêu cầu đầu vào cho người đánh máy để ghi chú thích kiểu về việc sử dụng S và K, ghi lại hiệu quả dẫn xuất. Nhưng đó là một lon giun khác ...

Đây là một nơi tốt để dừng lại, nếu bạn đã đủ quan tâm để bắt đầu. Phần còn lại là những thứ "hậu trường".

Tạo các loại tổ hợp

Tôi đã tạo các kiểu tổ hợp đó bằng cách sử dụng bản dịch trừu tượng trong ngoặc từ các thuật ngữ lý thuyết kiểu có liên quan. Để cho thấy cách tôi đã làm và làm cho bài đăng này không hoàn toàn vô nghĩa, hãy để tôi cung cấp thiết bị của mình.

Tôi có thể viết các loại tổ hợp, được tóm tắt đầy đủ về các tham số của chúng, như sau. Tôi sử dụng pilhàm tiện dụng của mình , kết hợp Pi và lambda để tránh lặp lại kiểu miền và thay vào đó, cho phép tôi sử dụng không gian hàm của Haskell để liên kết các biến một cách hữu ích. Có lẽ bạn gần như có thể đọc những điều sau đây!

pTy :: Tm a
pTy = fmap magic $
  pil Set $ \ _A -> pil (pil _A $ \ _ -> Set) $ \ _B -> Set

kTy :: Tm a
kTy = fmap magic $
  pil Set $ \ _G -> pil Set $ \ _A -> pil _A $ \ a -> pil _G $ \ g -> _A

sTy :: Tm a
sTy = fmap magic $
  pil Set $ \ _G ->
  pil (pil _G $ \ g -> Set) $ \ _A ->
  pil (pil _G $ \ g -> pil (_A :$ g) $ \ _ -> Set) $ \ _B ->
  pil (pil _G $ \ g -> pil (_A :$ g) $ \ a -> _B :$ g :$ a) $ \ f ->
  pil (pil _G $ \ g -> _A :$ g) $ \ a ->
  pil _G $ \ g -> _B :$ g :$ (a :$ g)

Với những điều này được xác định, tôi trích xuất các điều khoản con mở có liên quan và chạy chúng qua bản dịch.

Bộ công cụ mã hóa A de Bruijn

Đây là cách xây dựng pil. Đầu tiên, tôi định nghĩa một lớp các Fintập lặp, được sử dụng cho các biến. Mỗi tập hợp như vậy đều có phần embviền bảo toàn hàm tạo vào tập hợp ở trên, cộng với một topphần tử mới và bạn có thể phân biệt chúng: embdhàm cho bạn biết liệu một giá trị có trong hình ảnh của emb.

class Fin x where
  top :: Su x
  emb :: x -> Su x
  embd :: Su x -> Maybe x

Chúng tôi có thể, tất nhiên, thuyết minh Fincho ZeSuc

instance Fin Ze where
  top = Ze              -- Ze is the only, so the highest
  emb = magic
  embd _ = Nothing      -- there was nothing to embed

instance Fin x => Fin (Su x) where
  top = Su top          -- the highest is one higher
  emb Ze     = Ze            -- emb preserves Ze
  emb (Su x) = Su (emb x)    -- and Su
  embd Ze      = Just Ze           -- Ze is definitely embedded
  embd (Su x)  = fmap Su (embd x)  -- otherwise, wait and see

Bây giờ tôi có thể xác định ít hơn hoặc bằng, với một phép toán yếu dần .

class (Fin x, Fin y) => Le x y where
  wk :: x -> y

Các wkchức năng nên nhúng các yếu tố của xlớn nhất các yếu tố của y, do đó những điều bổ sung trong ynhỏ hơn, và do đó trong de về chỉ số Bruijn, ràng buộc địa phương hơn.

instance Fin y => Le Ze y where
  wk = magic    -- nothing to embed

instance Le x y => Le (Su x) (Su y) where
  wk x = case embd x of
    Nothing  -> top          -- top maps to top
    Just y   -> emb (wk y)   -- embedded gets weakened and embedded

Và một khi bạn đã sắp xếp xong điều đó, thì phần còn lại sẽ thực hiện phần còn lại.

lam :: forall x. Tm x -> ((forall y. Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
lam s f = Lam s (f (Var (wk (Ze :: Su x))))
pil :: forall x. Tm x -> ((forall y . Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
pil s f = Pi s (lam s f)

Hàm bậc cao hơn không chỉ cung cấp cho bạn một thuật ngữ đại diện cho biến, nó còn cung cấp cho bạn một thứ được nạp chồng trở thành đại diện chính xác của biến trong bất kỳ phạm vi nào mà biến có thể nhìn thấy. Đó là, thực tế là tôi gặp khó khăn trong việc phân biệt các phạm vi khác nhau theo loại cung cấp cho người đánh máy Haskell đủ thông tin để tính toán chuyển dịch cần thiết cho bản dịch sang biểu diễn de Bruijn. Tại sao lại nuôi chó và tự sủa?


điều này có thể cực kỳ ngớ ngẩn nhưng hình ảnh này sẽ thay đổi như thế nào nếu bạn thêm bộ Ftổ hợp? Fhoạt động khác nhau tùy thuộc vào đối số đầu tiên của nó: Nếu Alà một nguyên tử, MNlà các số hạng và PQlà một hợp, thì FAMN -> MF(PQ)MN -> NPQ. Điều này không thể được biểu diễn trong SK(I)giải tích nhưng Kcó thể được biểu diễn dưới dạng FF. Có thể mở rộng MLTT miễn phí điểm của bạn với điều này không?
kram1032

Tôi khá chắc rằng có vấn đề với quy trình trừu tượng hóa dấu ngoặc này, cụ thể là phần “tổ hợp trở thành hằng số”, dịch λx.c thành Kc cho tổ hợp c ∈ {S, K, U, P}. Vấn đề là các tổ hợp này là đa hình, và có thể được sử dụng ở kiểu phụ thuộc vào x; một loại như vậy không thể được bảo tồn bởi bản dịch này. Như một ví dụ cụ thể, thuật ngữ λ (A : Set) → λ (a : A) → akiểu (A : Set) → (a : A) → Ađược dịch sang S(S(KS)(KK))(KK), không thể được sử dụng ở kiểu mà kiểu của đối số thứ hai phụ thuộc vào đối số đầu tiên.
Anders Kaseorg

8

Tôi đoán "Tóm tắt dấu ngoặc" cũng hoạt động cho các loại phụ thuộc trong một số trường hợp. Trong phần 5 của bài báo sau, bạn tìm thấy một số loại K và S:

Sự trùng hợp kỳ lạ nhưng có ý nghĩa
Cú pháp và đánh giá kiểu an toàn phụ thuộc
Conor McBride, Đại học Strathclyde, 2010

Việc chuyển đổi một biểu thức lambda thành một biểu thức tổ hợp tương ứng với việc chuyển một chứng minh suy luận tự nhiên thành một chứng minh kiểu Hilbert.

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.