Một giải thích ngắn gọn nhưng đầy đủ về một hệ thống loại thuần túy / phụ thuộc là gì?


32

Nếu một cái gì đó đơn giản, thì nó nên được giải thích hoàn toàn với một vài từ. Điều này có thể được thực hiện cho phép tính:

Calcul-compus là một ngữ pháp cú pháp (về cơ bản, cấu trúc) với quy tắc rút gọn (có nghĩa là quy trình tìm kiếm / thay thế được áp dụng nhiều lần cho mỗi lần xuất hiện của một mẫu cụ thể cho đến khi không có mẫu nào như vậy tồn tại).

Ngữ pháp:

Term = (Term Term) | (λ Var . Term) | Var

Quy tắc giảm:

((λ var body) term) -> SUBS(body,var,term)
    where `SUBS` replaces all occurrences of `var`
    by `term` in `body`, avoiding name capture.

Ví dụ:

(λ a . a)                             -> (λ a a)
((λ a . (λ b . (b a))) (λ x . x))     -> (λ b . (b (λ x x)))
((λ a . (a a)) (λ x . x))             -> (λ x . x)
((λ a . (λ b . ((b a) a))) (λ x . x)) -> (λ b . ((b (λ x . x)) (λ x . x)))
((λ x . (x x)) (λ x . (x x)))         -> never halts

Mặc dù hơi không chính thức, người ta có thể lập luận rằng điều này đủ thông tin để một người bình thường hiểu toàn bộ tính toán - - và phải mất 22 dòng đánh dấu . Tôi đang cố gắng hiểu các hệ thống loại thuần / phụ thuộc được sử dụng bởi Idris / Agda và các dự án tương tự, nhưng lời giải thích rõ ràng hơn mà tôi có thể tìm thấy là Simply Easy - một bài báo hay, nhưng dường như có rất nhiều kiến ​​thức trước đó (Haskell, quy nạp định nghĩa) mà tôi không có. Tôi nghĩ rằng một cái gì đó tốt hơn, ít giàu hơn có thể loại bỏ một số những rào cản đó. Như vậy

Có thể đưa ra một lời giải thích ngắn gọn, đầy đủ về các hệ thống loại thuần / phụ thuộc, trong cùng định dạng tôi đã trình bày tính toán above ở trên không?


4
Các quy tắc của Hệ thống Pure Type rất ngắn gọn. Simply Easy là về việc thực hiện các loại phụ thuộc.

2
Vì vậy, nó không phải là "thù địch" theo nghĩa xúc phạm, nhưng theo nghĩa bạn nghĩ tôi đang đòi hỏi rất nhiều vì đã không thể hiện đủ nỗ lực trong việc tự mình tìm ra câu trả lời? Nếu đó là trường hợp, tôi đồng ý câu hỏi này có thể đòi hỏi rất nhiều vì vậy có lẽ nó chỉ là xấu. Nhưng cũng có rất nhiều nỗ lực đằng sau nó, bạn có nghĩ rằng tôi nên chỉnh sửa trong nỗ lực của mình không?
MaiaVictor

3
Tôi cũng bị xúc phạm, thay mặt cho các đồng tác giả của tôi, người đã viết văn bản "Hướng dẫn thực hiện một phép tính Lambda được gõ phụ thuộc", thay thế "Đơn giản dễ dàng" như một tiêu đề làm việc. Tôi đã viết kernel của mã, đó là một máy đánh chữ trong <100 dòng Haskell.

2
Sau đó, tôi chắc chắn thể hiện bản thân xấu. Tôi yêu tờ giấy "Đơn giản dễ dàng" và đang đọc nó trong mỗi giờ nghỉ từ vài ngày trước - đó là điều duy nhất trên thế giới mang lại cho tôi cảm giác một phần tôi bắt đầu hiểu chủ đề (và tin rằng tôi đã thử) . Nhưng tôi nghĩ nó nhắm vào một cộng đồng có nhiều kiến ​​thức hơn tôi và đó có thể là lý do tại sao tôi vẫn gặp khó khăn khi tham gia vào đó. Không có gì để làm với chất lượng của giấy, nhưng những hạn chế của riêng tôi.
MaiaVictor

1
@pigworker và mã là phần yêu thích của tôi về nó, chính xác bởi vì nó (liên quan đến lời giải thích bằng tiếng Anh) là một lời giải thích ngắn gọn hơn, nhưng đầy đủ, như tôi đã hỏi ở đây. Bạn có tình cờ có một bản sao mã tôi có thể tải xuống không?
MaiaVictor

Câu trả lời:


7

Khước từ

Điều này rất không chính thức, như bạn yêu cầu.

Ngữ pháp

Trong một ngôn ngữ được gõ phụ thuộc, chúng ta có một chất kết dính ở cấp độ loại cũng như ở cấp độ giá trị:

Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var

Thuật ngữ đánh máy tốt là một thuật ngữ có loại đính kèm, chúng tôi sẽ viết t ∈ σhoặc

σ
t

để chỉ ra rằng thuật ngữ tcó loại σ.

Quy tắc đánh máy

Để đơn giản, chúng tôi yêu cầu λ v. t ∈ ∀ (v : σ). τcả hai λliên kết cùng một biến ( vtrong trường hợp này).

Quy tắc:

t ∈ σ is well-formed if σ ∈ * and t is in normal form (0)

*            ∈ *                                                 (1)
∀ (v : σ). τ ∈ *             -: σ ∈ *, τ ∈ *                     (2)
λ v. t       ∈ ∀ (v : σ). τ  -: t ∈ τ                            (3)
f x          ∈ SUBS(τ, v, x) -: f ∈ ∀ (v : σ). τ, x ∈ σ          (4)
v            ∈ σ             -: v was introduced by ∀ (v : σ). τ (5)

Do đó, *là "loại của tất cả các loại" (1), các dạng từ loại (2), trừu tượng lambda có loại pi (3) và nếu vđược giới thiệu bởi ∀ (v : σ). τ, thì vcó loại σ(5).

"Ở dạng bình thường" có nghĩa là chúng tôi thực hiện càng nhiều lần giảm càng tốt bằng cách sử dụng quy tắc giảm:

"Quy tắc giảm"

(λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ) ~> SUBS(b, v, t) ∈ SUBS(τ, v, t)
    where `SUBS` replaces all occurrences of `v`
    by `t` in `τ` and `b`, avoiding name capture.

Hoặc theo cú pháp hai chiều trong đó

σ
t

có nghĩa là t ∈ σ:

(∀ (v : σ). τ) σ    SUBS(τ, v, t)
                 ~>
(λ  v     . b) t    SUBS(b, v, t)

Chỉ có thể áp dụng trừu tượng lambda cho một thuật ngữ khi thuật ngữ này có cùng loại với biến trong bộ định lượng forall liên quan. Sau đó, chúng tôi giảm cả trừu tượng lambda và định lượng forall theo cùng một cách như trong phép tính lambda thuần túy trước đây. Sau khi trừ phần cấp giá trị, chúng ta có quy tắc gõ (4).

Một ví dụ

Đây là toán tử ứng dụng chức năng:

∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ  A       B            f                    x     . f x

(chúng tôi viết tắt ∀ (x : σ). τđể σ -> τnếu τkhông đề cập đến x)

flợi nhuận B yđối với bất kỳ cung cấp ycác loại A. Chúng tôi áp dụng fđể x, đó là của đúng loại A, và thay thế ycho xtrong sau ., do đó f x ∈ SUBS(B y, y, x)~> f x ∈ B x.

Bây giờ chúng ta hãy viết tắt toán tử ứng dụng hàm appvà áp dụng nó cho chính nó:

∀ (A : *) (B : A -> *). ?
λ  A       B          . app ? ? (app A B)

Tôi đặt ?cho các điều khoản mà chúng tôi cần phải cung cấp. Đầu tiên chúng tôi giới thiệu rõ ràng và khởi tạo AB:

∀ (f : ∀ (y : A). B y) (x : A). B x
app A B

Bây giờ chúng ta cần thống nhất những gì chúng ta có

∀ (f : ∀ (y : A). B y) (x : A). B x

giống như

(∀ (y : A). B y) -> ∀ (x : A). B x

và những gì app ? ?nhận được

∀ (x : A'). B' x

Kết quả này trong

A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument

(xem thêm Dự đoán là gì? )

Biểu thức của chúng tôi (sau khi đổi tên) trở thành

∀ (A : *) (B : A -> *). ?
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)

Vì đối với bất kỳ A, Bf(ở đâu f ∈ ∀ (y : A). B y)

∀ (y : A). B y
app A B f

chúng ta có thể khởi tạo ABnhận (đối với bất kỳ loại nào fphù hợp)

∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f

và chữ ký loại tương đương với (∀ (x : A). B x) -> ∀ (x : A). B x.

Toàn bộ biểu thức là

∀ (A : *) (B : A -> *). (∀ (x : A). B x) -> ∀ (x : A). B x
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)

I E

∀ (A : *) (B : A -> *) (f : ∀ (x : A). B x) (x : A). B x
λ  A       B            f                    x     .
    app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B) f x

mà sau khi tất cả các mức giảm ở mức giá trị sẽ apptrả lại như cũ .

Vì vậy, trong khi nó đòi hỏi chỉ là một vài bước trong giải tích lambda tinh khiết để có được apptừ app app, trong một khung cảnh đánh máy (và đặc biệt là một lệ thuộc gõ), chúng tôi cũng cần phải quan tâm đến thống nhất và mọi thứ trở nên phức tạp hơn ngay cả với một số tiện không đồng nhất ( * ∈ *).

Kiểm tra loại

  • Nếu t*sau đó t ∈ *bằng cách (1)
  • Nếu t∀ (x : σ) τ, σ ∈? *, τ ∈? *(xem lưu ý về ∈?bên dưới) sau đó t ∈ *bởi (2)
  • Nếu tf x, f ∈ ∀ (v : σ) τđối với một số στ, x ∈? σsau đó t ∈ SUBS(τ, v, x)(4)
  • Nếu tlà một biến v, vđược giới thiệu bởi ∀ (v : σ). τsau đó t ∈ σbởi (5)

Tất cả đều là quy tắc suy luận, nhưng chúng ta không thể làm tương tự cho lambdas (suy luận kiểu là không thể áp dụng cho các loại phụ thuộc). Vì vậy, đối với lambdas, chúng tôi kiểm tra ( t ∈? σ) thay vì suy ra:

  • Nếu tđược λ v. bvà kiểm tra lại ∀ (v : σ) τ, b ∈? τthìt ∈ ∀ (v : σ) τ
  • Nếu tlà một cái gì đó khác và được kiểm tra lại σthì hãy suy ra kiểu tsử dụng hàm trên và kiểm tra xem nó có phải làσ

Kiểm tra sự bằng nhau cho các loại yêu cầu chúng phải ở dạng bình thường, vì vậy để quyết định xem tcó loại nào σtrước tiên chúng ta kiểm tra σloại đó không *. Nếu vậy, thì σcó thể bình thường hóa (nghịch lý của modulo Girard) và nó được chuẩn hóa (do đó σtrở nên được hình thành tốt bởi (0)). SUBScũng bình thường hóa các biểu thức để bảo toàn (0).

Điều này được gọi là kiểm tra loại hai chiều. Với nó, chúng ta không cần chú thích mỗi lambda với một loại: nếu trong f xloại fđược biết, thì xsẽ được kiểm tra đối với loại đối số fnhận được thay vì được suy ra và so sánh cho sự bình đẳng (cũng kém hiệu quả hơn). Nhưng nếu flà lambda, nó yêu cầu chú thích loại rõ ràng (chú thích được bỏ qua trong ngữ pháp và ở mọi nơi, bạn có thể thêm Ann Term Termhoặc λ' (σ : Term) (v : Var)vào các hàm tạo).

Ngoài ra, hãy xem đơn giản hơn, dễ dàng hơn! bài viết trên blog.


1
Phân biệt "Đơn giản hơn, dễ dàng hơn".

Quy tắc giảm đầu tiên trên forall trông kỳ lạ. Không giống như lambdas, foralls không nên được áp dụng theo cách đánh máy tốt (phải không?).

@chi, tôi không hiểu bạn đang nói gì Có lẽ ký hiệu của tôi là xấu: quy tắc rút gọn nói (λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ)~> SUBS(b, v, t) ∈ SUBS(τ, v, t).
dùng3237465

1
Tôi tìm thấy các ký hiệu sai lệch. Có vẻ như bạn có hai quy tắc: một cho vô nghĩa (∀ (v : σ). τ) t ~> ...một quy tắc khác cho ý nghĩa (λ v. b) t ~> .... Tôi sẽ loại bỏ cái đầu tiên, và biến nó thành một bình luận bên dưới.

1
Quy tắc (1) chứa kết luận của nó là tiền đề. Bạn có thể so sánh sự đơn giản của hệ thống của bạn với phiên bản hai chiều chỉ khi bạn có một hệ thống hoạt động. Bạn có thể nói bạn giữ mọi thứ bình thường, nhưng quy tắc của bạn thì không.

24

Chúng ta hãy đi. Tôi sẽ không bận tâm về nghịch lý của Girard, bởi vì nó làm sao lãng những ý tưởng trung tâm. Tôi sẽ cần phải giới thiệu một số máy móc trình bày về các phán đoán và dẫn xuất và như vậy.

Ngữ pháp

Thuật ngữ :: = (Loại bỏ) | * | (Var: Thời hạn) → Thời hạn | λVar↦Term

Loại bỏ :: = Thời hạn: Thời hạn | Var | Loại bỏ thời hạn

Ngữ pháp có hai dạng được xác định lẫn nhau, "thuật ngữ" là khái niệm chung của sự vật (loại là sự vật, giá trị là sự vật), bao gồm * (loại loại), loại chức năng phụ thuộc và trừu tượng lambda, nhưng cũng nhúng " loại bỏ "(nghĩa là" tập quán "chứ không phải là" công trình "), là các ứng dụng lồng nhau trong đó thứ cuối cùng ở vị trí chức năng là một biến hoặc một thuật ngữ được chú thích với loại của nó.

Quy tắc giảm

(λy↦t: (x: S) → T) s ↝ t [s: S / y]: T [s: S / x]

(t: T) t

Thao tác thay thế t [e / x] thay thế mọi lần xuất hiện của biến x bằng loại trừ e, tránh bắt tên. Để hình thành một ứng dụng có thể giảm, một thuật ngữ lambda phải được chú thích theo loại của nó để loại bỏ . Chú thích kiểu cho phép trừu tượng hóa lambda một loại "phản ứng", cho phép ứng dụng tiến hành. Khi chúng ta đạt đến điểm không có thêm ứng dụng nào xảy ra và hoạt động t: T đang được nhúng trở lại vào cú pháp thuật ngữ, chúng ta có thể bỏ chú thích loại.

Chúng ta hãy mở rộng mối quan hệ giảm bằng cách đóng cấu trúc: các quy tắc được áp dụng ở bất kỳ đâu trong các điều khoản và loại bỏ mà bạn có thể tìm thấy thứ gì đó phù hợp với phía bên trái. Viết ↝ * cho đóng cửa phản xạ-transitive (0-hoặc-nhiều-bước) của ↝. Hệ thống giảm kết quả là hợp lưu theo nghĩa này:

Nếu s ↝ * p và s ↝ * q, thì tồn tại một số r sao cho p ↝ * r và q ↝ * r.

Bối cảnh

Bối cảnh :: = | Bối cảnh, Var: Thuật ngữ

Các bối cảnh là các chuỗi gán các loại cho các biến, phát triển ở bên phải, mà chúng ta nghĩ là kết thúc "cục bộ", cho chúng ta biết về các biến bị ràng buộc gần đây nhất. Một thuộc tính quan trọng của bối cảnh là luôn có thể chọn một biến chưa được sử dụng trong ngữ cảnh. Chúng tôi duy trì bất biến rằng các biến được gán trong các kiểu trong ngữ cảnh là khác biệt.

Bản án

Phán quyết :: = Bối cảnh ⊢ Thời hạn có Thời hạn | Bối cảnh ⊢ Loại bỏ là hạn

Đó là ngữ pháp của các phán đoán, nhưng làm thế nào để đọc chúng? Để bắt đầu, là biểu tượng "quay đầu" truyền thống, tách biệt các giả định với kết luận: bạn có thể đọc nó một cách không chính thức như "nói".

G T có t

có nghĩa là bối cảnh đã cho G, loại T thừa nhận thuật ngữ t;

G ⊢ e là S

có nghĩa là bối cảnh đã cho G, loại bỏ e được đưa ra loại S.

Các phán đoán có cấu trúc thú vị: không hoặc nhiều đầu vào , một hoặc nhiều chủ đề , không hoặc nhiều đầu ra .

INPUTS                   SUBJECT        OUTPUTS
Context |- Term   has    Term
Context |-               Elim      is   Term

Đó là, chúng tôi phải đề xuất các loại thuật ngữ trước và chỉ cần kiểm tra chúng, nhưng chúng tôi tổng hợp các loại loại bỏ.

Quy tắc đánh máy

Tôi trình bày những thứ này theo phong cách Prolog mơ hồ, viết J -: P1; ...; Pn để chỉ ra rằng phán đoán J giữ nếu cơ sở từ P1 đến Pn cũng được giữ. Một tiền đề sẽ là một bản án khác, hoặc một yêu cầu về giảm.

Điều kiện

G ⊢ T có t -: T R; G R có t

G * có *

G ⊢ * có (x: S) → T -: G ⊢ * có S; G, z: S! - * có T [z / x]

G ⊢ (x: S) → T có y↦t -: G, z: S ⊢ T [z / x] có t [z / y]

G T có (e) -: G e là T

Loại bỏ

G ⊢ e là R -: G e là S; S ↝ R

G, x: S, G '⊢ x là S

G ⊢ fs là T [s: S / x] -: G ⊢ f là (x: S) → T; G S có s

Và đó là nó!

Chỉ có hai quy tắc không được định hướng theo cú pháp: quy tắc có nội dung "bạn có thể giảm một loại trước khi sử dụng nó để kiểm tra một thuật ngữ" và quy tắc "bạn có thể giảm một loại sau khi bạn đã tổng hợp loại đó sau khi loại bỏ". Một chiến lược khả thi là giảm các loại cho đến khi bạn tiếp xúc với nhà xây dựng hàng đầu.

Hệ thống này không được bình thường hóa mạnh mẽ (vì Nghịch lý của Girard, đó là một nghịch lý kiểu nói dối tự tham chiếu), nhưng nó có thể được thực hiện bình thường hóa mạnh mẽ bằng cách tách * thành "cấp độ vũ trụ" trong đó bất kỳ giá trị nào liên quan đến các loại ở cấp độ thấp hơn có loại ở cấp độ cao hơn, ngăn chặn tự tham khảo.

Hệ thống này, tuy nhiên, có đặc tính bảo quản kiểu, theo nghĩa này.

Nếu G T có t và G ↝ * D và T ↝ * R và t ↝ r, thì D R có r.

Nếu G ⊢ e là S và G * D và e ↝ f, thì tồn tại R sao cho S ↝ * R và D f là R.

Các bối cảnh có thể tính toán bằng cách cho phép các thuật ngữ mà chúng chứa để tính toán. Nghĩa là, nếu phán đoán có hiệu lực ngay bây giờ, bạn có thể tính toán đầu vào của nó bao nhiêu tùy thích và chủ đề của nó một bước, và sau đó có thể tính toán đầu ra của nó bằng cách nào đó để đảm bảo phán đoán kết quả vẫn hợp lệ. Bằng chứng là một cảm ứng đơn giản về các dẫn xuất gõ, với sự hợp lưu của -> *.

Tất nhiên, tôi chỉ trình bày lõi chức năng ở đây, nhưng phần mở rộng có thể khá mô-đun. Đây là cặp.

Thuật ngữ :: = ... | (x: S) * T | s, t

Loại bỏ :: = ... | e.head | đuôi

(s, t: (x: S) * T) .head s: S

(s, t: (x: S) * T). đuôi t: T [s: S / x]

G ⊢ * có (x: S) * T -: G ⊢ * có S; G, z: S * có T [z / x]

G ⊢ (x: S) * T có s, t -: G ⊢ S có s; G ⊢ T [s: S / x] có t

G ⊢ e.head là S -: G e là (x: S) * T

G ⊢ e.tail là T [e.head / x] -: G ⊢ e là (x: S) * T


1
G, x:S, G' ⊢ x is S -: G' ⊬ x?
user3237465

1
@ user3237465 Không. Cảm ơn! Đã sửa. (Khi tôi thay thế các cửa quay nghệ thuật ascii bằng các cửa quay html (do đó làm cho chúng vô hình trên điện thoại của tôi; xin lỗi nếu điều đó xảy ra ở nơi khác) Tôi đã bỏ lỡ điều đó.)

1
Ồ, tôi nghĩ rằng bạn chỉ đang chỉ ra lỗi đánh máy. Quy tắc nói rằng, đối với mỗi biến trong ngữ cảnh, chúng tôi tổng hợp loại mà bối cảnh gán cho nó. Khi giới thiệu bối cảnh, tôi đã nói "Chúng tôi duy trì bất biến rằng các biến được gán cho các loại trong ngữ cảnh là khác biệt." vì vậy bóng là không được phép. Bạn sẽ thấy rằng mỗi khi các quy tắc mở rộng bối cảnh, họ luôn chọn một chữ "z" mới, tạo ra bất kỳ ràng buộc nào mà chúng ta đang bước theo. Shadowing là anathema. Nếu bạn có bối cảnh x: *, x: x thì kiểu của x cục bộ hơn sẽ không còn nữa vì đó là x ngoài phạm vi.

1
Tôi chỉ muốn bạn và những người trả lời khác biết rằng tôi sẽ quay lại chủ đề này mỗi khi nghỉ làm. Tôi thực sự muốn học điều này, và lần đầu tiên tôi cảm thấy như mình thực sự nhận được phần lớn. Bước tiếp theo sẽ là thực hiện và viết một vài chương trình. Tôi rất vui khi có thể sống trong thời đại mà thông tin về những chủ đề tuyệt vời như vậy có sẵn trên toàn cầu cho một người như tôi, và tất cả là nhờ những thiên tài như bạn đã dành thời gian của cuộc đời để truyền bá kiến ​​thức đó, cho miễn phí, trên internet. Xin lỗi lần nữa vì đã đặt câu hỏi của tôi không tốt, và cảm ơn bạn!
MaiaVictor

1
@cody Có, không có mở rộng. Để xem tại sao không cần thiết, lưu ý rằng hai quy tắc tính toán cho phép bạn triển khai chiến lược nơi bạn bình thường hóa hoàn toàn các loại trước khi bạn kiểm tra các điều khoản và bạn cũng bình thường hóa các loại ngay sau khi tổng hợp chúng khỏi bị loại bỏ. Vì vậy, trong quy tắc mà các loại phải khớp, chúng đã được chuẩn hóa, do đó bằng nhau trên mũi nếu các loại được kiểm tra và tổng hợp "ban đầu" có thể chuyển đổi được. Trong khi đó, việc hạn chế kiểm tra đẳng thức chỉ ở nơi đó là ổn vì thực tế này: nếu T có thể chuyển đổi thành loại chính tắc, thì nó sẽ giảm xuống thành loại chính tắc.
thợ lợn

8

Sự tương ứng của Curry-Howard nói rằng có một sự tương ứng có hệ thống giữa các hệ thống loạihệ thống bằng chứng trong logic. Theo quan điểm trung tâm của lập trình viên về điều này, bạn có thể làm lại theo cách này:

  • Hệ thống bằng chứng logic là ngôn ngữ lập trình.
  • Các ngôn ngữ này được gõ tĩnh.
  • Trách nhiệm của hệ thống loại trong ngôn ngữ như vậy là cấm các chương trình xây dựng bằng chứng không có căn cứ.

Nhìn từ góc độ này:

  • Tính toán lambda chưa được đánh dấu mà bạn tóm tắt không có hệ thống loại quan trọng, do đó, một hệ thống bằng chứng được xây dựng trên nó sẽ không có cơ sở.
  • Phép tính lambda được gõ đơn giản là một ngôn ngữ lập trình có tất cả các loại cần thiết để xây dựng bằng chứng âm thanh theo logic cảm tính ("if / then", "và", "hoặc", "not"). Nhưng các loại của nó không đủ tốt để kiểm tra bằng chứng liên quan đến các bộ lượng hóa ("cho tất cả x, ..."; "tồn tại một x sao cho ...").
  • Lệ thuộc gõ phép tính lambda có các loại và các quy tắc hỗ trợ sentential Logic bậc nhất quantifiers (định lượng trên giá trị).

Dưới đây là các quy tắc khấu trừ tự nhiên cho logic thứ nhất, sử dụng sơ đồ từ mục nhập Wikipedia về khấu trừ tự nhiên . Về cơ bản, đây là các quy tắc của một phép tính lambda phụ thuộc tối thiểu.

Khấu trừ tự nhiên đầu tiên

Lưu ý rằng các quy tắc có các điều khoản lambda trong đó. Chúng có thể được đọc như các chương trình xây dựng bằng chứng của các câu được biểu thị bằng các loại của chúng (hoặc ngắn gọn hơn, chúng tôi chỉ nói các chương trình là bằng chứng ). Các quy tắc giảm tương tự mà bạn đưa ra có thể được áp dụng cho các điều khoản lambda này.


Tại sao chúng ta quan tâm đến điều này? Chà, trước hết, bởi vì bằng chứng có thể trở thành một công cụ hữu ích trong lập trình và có một ngôn ngữ có thể hoạt động với bằng chứng khi các đối tượng hạng nhất mở ra nhiều con đường. Ví dụ, nếu chức năng của bạn có một điều kiện tiên quyết, thay vì viết nó dưới dạng một nhận xét, bạn thực sự có thể yêu cầu một bằng chứng về nó như là một đối số.

Thứ hai, bởi vì máy móc hệ thống loại cần thiết để xử lý các bộ lượng hóa có thể có những ứng dụng khác trong bối cảnh lập trình. Cụ thể, các ngôn ngữ được gõ phụ thuộc xử lý các bộ lượng tử phổ ("cho tất cả x, ...") bằng cách sử dụng một khái niệm gọi là loại hàm phụ thuộc Hàm hàma trong đó kiểu tĩnh của kết quả có thể phụ thuộc vào giá trị thời gian chạy của đối số.

Để cung cấp cho ứng dụng rất dành cho người đi bộ này, tôi viết mã mọi lúc phải đọc các tệp Avro bao gồm các bản ghi có cấu trúc thống nhất, tất cả đều chia sẻ cùng một bộ tên và loại trường. Điều này đòi hỏi tôi phải:

  1. Hardcode cấu trúc của các bản ghi trong chương trình dưới dạng một bản ghi.
    • Ưu điểm: Mã đơn giản hơn và trình biên dịch có thể bắt lỗi trong mã của tôi
    • Nhược điểm: Chương trình được mã hóa cứng để đọc các tệp phù hợp với loại bản ghi.
  2. Đọc lược đồ của dữ liệu trong thời gian chạy, biểu diễn nó một cách tổng quát như một cấu trúc dữ liệu và sử dụng nó để xử lý các bản ghi một cách tổng quát
    • Ưu điểm: Chương trình của tôi không được mã hóa thành chỉ một loại tệp
    • Nhược điểm: Trình biên dịch không thể bắt được nhiều lỗi.

Như bạn có thể thấy trong trang hướng dẫn Avro Java , họ chỉ cho bạn cách sử dụng thư viện theo cả hai cách tiếp cận này.

Với các loại chức năng phụ thuộc, bạn có thể có bánh của bạn và ăn nó, với chi phí của một hệ thống loại phức tạp hơn. Bạn có thể viết một hàm đọc tệp Avro, trích xuất lược đồ và trả về nội dung của tệp dưới dạng luồng bản ghi có kiểu tĩnh phụ thuộc vào lược đồ được lưu trong tệp . Trình biên dịch sẽ có thể bắt lỗi trong đó, ví dụ, tôi đã cố truy cập vào một trường có tên không tồn tại trong các bản ghi của các tệp mà nó sẽ xử lý khi chạy. Ngọt hả


1
Xây dựng các loại trong thời gian chạy theo cách bạn đề cập là một cái gì đó thực sự tuyệt vời mà tôi chưa từng nghĩ đến. Thật ngọt ngào! Cảm ơn câu trả lời sâu sắc.
MaiaVictor
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.