MathLabs

Nền tảng toán học

Lý thuyết kiểu

Một nền tảng cho toán học và tính toán trong đó mỗi hạng đều mang một kiểu, dùng trong các trợ lý chứng minh.

Trực giácMọi giá trị đều có nhãn

Trong hầu hết ngôn ngữ lập trình, 33 là `int` và `"hello"` là `string` — trình biên dịch từ chối `3 + "hello"` trước khi chương trình chạy. Lý thuyết kiểu biến ý tưởng thường ngày này thành một nền tảng cho toàn bộ toán học: thay vì bắt đầu từ tập hợp và quan hệ thuộc (x∈Ax \in A), bắt đầu từ hạng và kiểu (t:At : A, "tt có kiểu AA"). Đáng chú ý, khi kiểu đủ phong phú, một kiểu có thể mã hóa một mệnh đề toán học, và một hạng của kiểu đó trở thành một chứng minh — chương trình và chứng minh trở thành cùng một loại đối tượng.

Sơ đồ cây của một dẫn xuất kiểu với các phán đoán kiểu tại mỗi đỉnh.
Một dẫn xuất kiểu dưới dạng cây: mỗi đỉnh là một phán đoán Γ⊢t:A\Gamma \vdash t : A, mỗi cạnh là một quy tắc kiểu nối hạng với các hạng con.

Đại họcPhép tính lambda đơn định kiểu

Định nghĩa: Phán đoán kiểu

Một phán đoán kiểu Γ⊢t:A\Gamma \vdash t : A nói "trong ngữ cảnh Γ\Gamma (danh sách cặp biến-kiểu x1:A1,…,xn:Anx_1:A_1,\dots,x_n:A_n), hạng tt có kiểu AA". Các quy tắc cốt lõi: một biến có kiểu mà ngữ cảnh gán cho nó; một trừu tượng hóa λx:A. t\lambda x{:}A.\,t có kiểu A→BA\to B bất cứ khi nào tt có kiểu BB trong ngữ cảnh mở rộng bởi x:Ax:A; và áp dụng f uf\,u có kiểu BB bất cứ khi nào f:A→Bf:A\to B và u:Au:A.

Γ,x:A⊢t:BΓ⊢λx:A. t:A→BΓ⊢f:A→BΓ⊢u:AΓ⊢f u:B\dfrac{\Gamma, x{:}A \vdash t : B}{\Gamma \vdash \lambda x{:}A.\,t : A\to B} \qquad \dfrac{\Gamma \vdash f : A\to B \quad \Gamma \vdash u : A}{\Gamma \vdash f\,u : B}

Lý thuyết kiểu phụ thuộc Martin-Löf tổng quát hóa điều này xa hơn: kiểu kết quả của một hàm có thể phụ thuộc vào giá trị của đối số nó. **Kiểu Π\Pi** (kiểu hàm phụ thuộc) ∏x:AB(x)\prod_{x:A} B(x) gom các hàm gửi mỗi x:Ax:A tới một hạng kiểu B(x)B(x) — khi BB không nhắc tới xx, nó suy biến thành A→BA\to B thông thường. Đối ngẫu, **kiểu Σ\Sigma** (kiểu cặp phụ thuộc) ∑x:AB(x)\sum_{x:A} B(x) gom các cặp ⟨a,b⟩\langle a,b\rangle với a:Aa:A và b:B(a)b:B(a) — một cặp mà kiểu thành phần thứ hai phụ thuộc vào thành phần thứ nhất; khi BB không nhắc tới xx, nó suy biến thành tích A×BA\times B thông thường.

∏x:AB(x),∑x:AB(x)\prod_{x:A} B(x), \qquad \sum_{x:A} B(x)
Kiểu phụ thuộc và cách đọc Curry–Howard
Kiến trúc kiểuTrường hợp riêng không phụ thuộcCách đọc Curry–Howard
∏x:AB(x)\prod_{x:A} B(x)A→BA\to B (khi BB hằng)∀x:A, B(x)\forall x{:}A,\ B(x)
∑x:AB(x)\sum_{x:A} B(x)A×BA\times B (khi BB hằng)∃x:A, B(x)\exists x{:}A,\ B(x)
Tiên đề đơn trị: (A≃B)≃(A=UB)(A\simeq B) \simeq (A =_{\mathcal U} B)—Các kiểu tương đương được đồng nhất

Nếu Γ⊢t:A\Gamma \vdash t : A trong phép tính lambda đơn định kiểu, thì tt là chuẩn hóa mạnh: mọi dãy thu gọn β\beta bắt đầu từ tt đều kết thúc sau hữu hạn bước.

Vì sao đúng?

Đây là lý do vì sao phép tính lambda đơn định kiểu, dù có áp dụng hàm và lồng kiểu đệ quy, không đầy đủ Turing — không hạng được định kiểu nào có thể chạy mãi, chính điều này làm trình kiểm tra kiểu trong mảnh này luôn dừng.

Chứng minh

(Phương pháp khả quy Tait.) Định nghĩa, bằng quy nạp theo cấu trúc kiểu, một tập REDA\mathrm{RED}_A các "hạng khả quy" cho mỗi kiểu AA: với kiểu cơ sở oo, đặt REDo\mathrm{RED}_o là tập SNSN gồm mọi hạng chuẩn hóa mạnh; với kiểu hàm, đặt REDA→B={t:for every u∈REDA, t u∈REDB}\mathrm{RED}_{A\to B} = \{t : \text{for every } u \in \mathrm{RED}_A,\ t\,u \in \mathrm{RED}_B\}.

Trước hết ta chứng minh, bằng quy nạp theo AA, ba tính chất kỹ thuật cùng lúc: (CR1) mọi hạng trong REDA\mathrm{RED}_A đều chuẩn hóa mạnh; (CR2) REDA\mathrm{RED}_A đóng dưới thu gọn β\beta (nếu t∈REDAt\in\mathrm{RED}_A và t→t′t\to t' thì t′∈REDAt'\in\mathrm{RED}_A); (CR3) mọi hạng "trung tính" (một biến hay áp dụng, không phải trừu tượng hóa) mà mọi thu gọn một bước của nó đã nằm trong REDA\mathrm{RED}_A thì chính nó cũng nằm trong REDA\mathrm{RED}_A. Trường hợp cơ sở hiển nhiên từ định nghĩa SNSN; trường hợp kiểu hàm khai triển định nghĩa REDA→B\mathrm{RED}_{A\to B} và dùng giả thiết quy nạp trên các kiểu nhỏ hơn AA và BB.

Tiếp theo, ta chỉ ra mọi hạng được định kiểu đều khả quy dưới bất kỳ phép thế nào của các hạng khả quy cho biến tự do của nó: bằng quy nạp theo dẫn xuất kiểu của Γ⊢t:A\Gamma \vdash t:A, nếu σ\sigma gán mỗi biến xx trong Γ\Gamma một hạng σ(x)∈REDΓ(x)\sigma(x)\in\mathrm{RED}_{\Gamma(x)}, thì tσ∈REDAt\sigma \in \mathrm{RED}_A. Trường hợp biến hiển nhiên. Trường hợp áp dụng theo trực tiếp từ định nghĩa REDA→B\mathrm{RED}_{A\to B}. Trường hợp trừu tượng hóa cần cẩn thận nhất: với (λx. t)σ(\lambda x.\,t)\sigma áp lên u∈REDAu\in\mathrm{RED}_A bất kỳ, hạng thu gọn trong một bước β\beta về t(σ,x:=u)t(\sigma,x{:=}u), nằm trong REDB\mathrm{RED}_B theo giả thiết quy nạp (áp cho phép thế mở rộng); tính chất (CR3) — đóng dưới "mở rộng" cho các hạng mà mọi thu gọn đã khả quy — khi đó đặt chính (λx. t)σ u(\lambda x.\,t)\sigma\,u vào REDB\mathrm{RED}_B, nên (λx. t)σ∈REDA→B(\lambda x.\,t)\sigma \in \mathrm{RED}_{A\to B} theo định nghĩa.

Cuối cùng, áp dụng điều này cho phép thế đồng nhất: mỗi biến x:Ax{:}A tự nó trung tính và không có thu gọn nào cả, nên nó nằm trong REDA\mathrm{RED}_A một cách hiển nhiên theo (CR3). Vậy với mọi dẫn xuất đóng Γ⊢t:A\Gamma \vdash t:A, lấy σ\sigma gửi mỗi xx tới chính nó cho t=tσ∈REDAt = t\sigma \in \mathrm{RED}_A. Theo (CR1), REDA⊆SN\mathrm{RED}_A \subseteq SN, nên t∈SNt \in SN: mọi hạng được định kiểu đều chuẩn hóa mạnh. ■\blacksquare

Nâng caoSự tương ứng Curry–Howard

Đọc A→BA\to B là "nếu AA thì BB", đọc A×BA\times B là "AA và BB", và một chứng minh của một mệnh đề trở thành một chương trình được định kiểu đúng với kiểu tương ứng — đây là mệnh đề-là-kiểu, chứng minh-là-chương trình. Kiểm tra chứng minh quy về kiểm tra kiểu của một hạng; xây chứng minh quy về viết chương trình đúng kiểu.

Cho φ\varphi là công thức mệnh đề xây từ nguyên tử, ∧\wedge, và →\to, và cho AφA_\varphi là kiểu đơn thu được bằng cách dịch nguyên tử thành kiểu cơ sở, ∧\wedge thành tích ×\times, và →\to thành kiểu hàm →\to. Khi đó φ\varphi chứng minh được trong logic mệnh đề trực giác khi và chỉ khi AφA_\varphi có cư dân: tồn tại một hạng đóng tt với ⊢t:Aφ\vdash t : A_\varphi.

Vì sao đúng?

Đây là phát biểu chính xác đằng sau câu "trợ lý chứng minh chỉ là một trình kiểm tra kiểu": Coq, Agda, và Lean chấp nhận một chứng minh được máy kiểm tra của một định lý đúng khi chúng chấp nhận một hạng của kiểu tương ứng, và kiểm tra kiểu (khác với chứng minh định lý nói chung) là một thuật toán nhỏ, nhanh, máy móc, và rất đáng tin cậy.

Chứng minh

(Chứng minh trở thành chương trình.) Bằng quy nạp theo dẫn xuất suy luận tự nhiên của ⊢φ\vdash \varphi. Nếu quy tắc cuối là dẫn nhập →\to, dẫn φ=ψ→χ\varphi=\psi\to\chi từ một chứng minh con của χ\chi dưới giả thiết ψ\psi: theo giả thiết quy nạp chứng minh con đó dịch thành hạng tt với x:Aψ⊢t:Aχx{:}A_\psi \vdash t : A_\chi (giả thiết trở thành biến tự do xx); khi đó λx:Aψ. t\lambda x{:}A_\psi.\,t đóng và có kiểu Aψ→Aχ=AφA_\psi\to A_\chi=A_\varphi. Nếu quy tắc cuối là loại bỏ →\to (modus ponens) từ ψ→χ\psi\to\chi và ψ\psi: theo giả thiết quy nạp ta có các hạng t1:Aψ→Aχt_1 : A_\psi\to A_\chi và t2:Aψt_2 : A_\psi; khi đó t1 t2:Aχt_1\,t_2 : A_\chi. Các quy tắc dẫn nhập ∧\wedge và loại bỏ ∧\wedge dịch đối xứng thành ghép cặp ⟨t1,t2⟩\langle t_1,t_2\rangle và hai phép chiếu. Mọi quy tắc của hệ chứng minh đều có quy tắc xây hạng tương ứng, nên quy nạp theo dẫn xuất cho ra một hạng đóng được định kiểu thuộc kiểu AφA_\varphi.

(Chương trình trở thành chứng minh.) Ngược lại, bằng quy nạp theo dẫn xuất kiểu ⊢t:A\vdash t : A. Mỗi quy tắc kiểu — biến, trừu tượng hóa, áp dụng, ghép cặp, phép chiếu — có đúng hình dạng cây như một quy tắc suy luận tự nhiên — giả thiết, dẫn nhập →\to, loại bỏ →\to, dẫn nhập ∧\wedge, loại bỏ ∧\wedge. Vậy lấy cùng cây dẫn xuất, xóa mọi hạng, và đọc mỗi kiểu như công thức nó dịch từ: đây chính xác là một chứng minh trực giác hợp lệ của φ\varphi (với A=AφA=A_\varphi).

Vì hai phép dịch (chứng minh →\to hạng, và hạng →\to chứng minh) là nghịch đảo cấu trúc của nhau trên các cây dẫn xuất — mỗi phép hoàn tác chính xác điều phép kia làm, quy tắc theo quy tắc — φ\varphi chứng minh được đúng khi AφA_\varphi có cư dân. ■\blacksquare

Đại họcỨng dụng thực tiễn và Ví dụ minh họa

Hệ kiểu phong phú bắt được cả lớp lỗi trước khi chương trình chạy: kiểu sở hữu của Rust ngăn dùng-sau-giải-phóng và tranh chấp dữ liệu ngay lúc biên dịch, và ngôn ngữ định kiểu phụ thuộc có thể mã hóa bất biến như "danh sách này có đúng nn phần tử" hay "chỉ số này trong khoảng hợp lệ" trực tiếp vào kiểu, khiến một số lỗi runtime không thể biểu diễn được. Ứng dụng sâu nhất nằm chính trong trợ lý chứng minh: Coq, Agda, và Lean có lõi nhỏ, được kiểm toán cẩn thận, chỉ làm mỗi việc kiểm tra kiểu hạng — vì sự tương ứng Curry–Howard, chấp nhận một hạng thuộc kiểu của một định lý chính là chấp nhận một chứng minh được máy kiểm tra, nên tin một kết quả toán học cỡ định lý Feit–Thompson hay định lý bốn màu quy về tin vài trăm dòng mã kiểm tra kiểu, không phải các chiến thuật (lớn hơn, khó kiểm toán hơn nhiều) đã xây nên hạng chứng minh.

Ví dụ: Một kiểu Π\Pi mà kiểu hàm thường không diễn đạt được

Cho Vec A n\mathrm{Vec}\,A\,n là kiểu danh sách độ dài nn gồm phần tử kiểu AA. Viết một hạng cư trú ∏n:N(Vec A n→Vec A n)\prod_{n:\mathbb N} (\mathrm{Vec}\,A\,n \to \mathrm{Vec}\,A\,n) — "với mọi độ dài nn, một hàm trên vector độ dài nn" — và giải thích vì sao kiểu đơn (không có Π\Pi) hoàn toàn không diễn đạt được đặc tả này.

Lời giải

Hạng λn:N. λv:Vec A n. v\lambda n{:}\mathbb N.\,\lambda v{:}\mathrm{Vec}\,A\,n.\,v hoạt động: với mỗi số tự nhiên nn, nó nhận một vector độ dài nn là vv và trả về nguyên vẹn — rõ ràng được định kiểu tốt, vì v:Vec A nv : \mathrm{Vec}\,A\,n được trả về ở kiểu Vec A n\mathrm{Vec}\,A\,n, với bất kỳ nn nào được cung cấp.

Điều làm đây là một sử dụng thật sự của Π\Pi thay vì kiểu hàm thường là kiểu của đối số thứ hai (Vec A n\mathrm{Vec}\,A\,n) nhắc tới giá trị của đối số thứ nhất (nn). Trong phép tính lambda đơn định kiểu, kiểu đối số và kết quả của một hàm được cố định một lần cho mãi mãi — A→BA\to B với A,BA,B chọn trước khi biết giá trị nào của kiểu AA. Không có cách nào viết "cho tôi số tự nhiên nn, và tùy vào số bạn cho, tôi sẽ đòi một vector đúng độ dài đó" chỉ dùng →\to: bạn cần một kiểu cố định duy nhất Vec A n\mathrm{Vec}\,A\,n nào đó hoạt động cho mọi nn cùng lúc, không phải điều Vec\mathrm{Vec} có nghĩa.

Cụ thể, nếu bạn thử viết điều này bằng kiểu hàm thường, tốt nhất bạn được N→(Vec A ?→Vec A ?)\mathbb N \to (\mathrm{Vec}\,A\,? \to \mathrm{Vec}\,A\,?) với một độ dài giữ chỗ CỐ ĐỊNH — vô dụng, vì nó sẽ từ chối vector độ dài khác, hoặc chấp nhận vector sai độ dài. Kiểu Π\Pi, ∏n:N(⋯ )\prod_{n:\mathbb N}(\cdots), chính xác là kiến trúc kiểu cho phép miền đích "nhìn lại" giá trị cụ thể của nn nhận được, đúng là sự phụ thuộc mà kiểu đơn không diễn đạt được.

Ví dụ: Curry–Howard cho phép hợp thành hàm

Công thức (A→B)→((B→C)→(A→C))(A\to B)\to((B\to C)\to(A\to C)) diễn đạt tính bắc cầu của kéo theo. Theo Curry–Howard, tìm hạng λ\lambda chứng minh nó, và kiểm tra dẫn xuất kiểu từng bước.

Lời giải

Hạng là t=λf:A→B. λg:B→C. λx:A. g (f x)t = \lambda f{:}A\to B.\,\lambda g{:}B\to C.\,\lambda x{:}A.\,g\,(f\,x) — chính xác là hợp thành hàm g∘fg\circ f áp lên xx.

Dẫn xuất kiểu từ trong ra ngoài. Trong ngữ cảnh f:A→B, g:B→C, x:Af{:}A\to B,\,g{:}B\to C,\,x{:}A: vì f:A→Bf:A\to B và x:Ax:A, áp dụng cho f x:Bf\,x:B. Vì g:B→Cg:B\to C và f x:Bf\,x:B, áp dụng cho g (f x):Cg\,(f\,x):C.

Giờ giải phóng các trừu tượng hóa từ trong ra ngoài. λx:A. g (f x)\lambda x{:}A.\,g\,(f\,x) có kiểu A→CA\to C trong ngữ cảnh f:A→B, g:B→Cf{:}A\to B,\,g{:}B\to C. Rồi λg:B→C. (⋯ )\lambda g{:}B\to C.\,(\cdots) có kiểu (B→C)→(A→C)(B\to C)\to(A\to C) trong ngữ cảnh f:A→Bf{:}A\to B. Cuối cùng λf:A→B. (⋯ )\lambda f{:}A\to B.\,(\cdots) có kiểu (A→B)→((B→C)→(A→C))(A\to B)\to((B\to C)\to(A\to C)) không còn giả thiết tự do nào — một hạng đóng.

Vậy ⊢t:(A→B)→((B→C)→(A→C))\vdash t : (A\to B)\to((B\to C)\to(A\to C)), nghĩa là tt là một chứng minh Curry–Howard của tính bắc cầu: "nếu AA kéo theo BB, và BB kéo theo CC, thì AA kéo theo CC" — cùng hạng mà một lập trình viên hàm sẽ viết để hợp thành hai hàm.

Nghiên cứuNghiên cứu hiện nay

Trong phép tính lambda đơn định kiểu, nếu f:A→Bf : A \to B và a:Aa : A, kiểu của f af\,a là gì?

Vì sao một trợ lý chứng minh như Coq, Agda, hay Lean có thể tin một chứng minh được máy kiểm tra dài hàng nghìn dòng?

Khi B(x)B(x) thực ra không phụ thuộc xx, ∏x:AB(x)\prod_{x:A} B(x) suy biến thành gì?

Tiên đề đơn trị (A≃B)≃(A=UB)(A\simeq B) \simeq (A =_{\mathcal U} B) nói rằng...

Tài liệu tham khảo

  1. The Univalent Foundations Program (2013). Homotopy Type Theory: Univalent Foundations of Mathematics
  2. Wikipedia contributors (2024). Curry–Howard correspondence
  3. Wikipedia contributors (2024). Intuitionistic type theory