Chúng ta có được gì? Hmm. Tình trạng của người độc thân là của giải pháp khó xử nhưng hiện tại cần thiết và chúng tôi sớm có thể làm việc với họ, càng tốt.
Hãy để tôi xem tôi có thể làm rõ hình ảnh không. Chúng tôi có một kiểu dữ liệu Nat
:
data Nat = Zero | Suc Nat
(chiến tranh đã được bắt đầu trên thậm chí tầm thường nhiều vấn đề hơn số lượng 'của c trong Suc
)
Loại Nat
có giá trị thời gian chạy mà không thể phân biệt tại loại cấp.Hệ thống kiểu Haskell hiện có thuộc tính thay thế, có nghĩa là trong bất kỳ chương trình được nhập tốt nào, bạn có thể thay thế bất kỳ biểu thức con được gõ tốt bằng cách hiển thị phụ thay thế với cùng phạm vi và loại, và chương trình sẽ tiếp tục được đánh máy tốt. Ví dụ, bạn có thể viết lại mỗi lần xuất hiện của
if <b> then <t> else <e>
để
if <b> then <e> else <t>
và bạn có thể chắc chắn rằng không có gì sẽ đi sai ... với kết quả kiểm tra kiểu của chương trình của bạn.
Thuộc tính thay thế là một sự bối rối. Đó là bằng chứng rõ ràng rằng hệ thống kiểu của bạn bỏ cuộc ngay lúc đó ý nghĩa bắt đầu thành vấn đề.
Bây giờ, bằng cách là loại dữ liệu cho giá trị thời gian chạy, Nat
cũng trở thành một loại giá trị loại cấp 'Zero
và 'Suc
. Sau này chỉ sống trong ngôn ngữ kiểu của Haskell và không có sự hiện diện thời gian chạy nào cả. Xin lưu ý rằng mặc dù 'Zero
và 'Suc
tồn tại ở cấp loại, nhưng không hữu ích khi gọi chúng là "loại" và những người hiện đang thực hiện điều đó phải tồn tại. Họ không có loại *
và do đó không thể phân loại các giá trị đó là những loại xứng đáng với tên.
Không có phương tiện trao đổi trực tiếp giữa thời gian chạy và loại Nat
s, điều này có thể gây phiền toái. Ví dụ mẫu mực liên quan đến một hoạt động quan trọng trên vectơ:
data Vec :: Nat -> * -> * where
VNil :: Vec 'Zero x
VCons :: x -> Vec n x -> Vec ('Suc n) x
Chúng tôi có thể muốn tính toán một vector của các bản sao của một nguyên tố (có lẽ như một phần của một thể hiện Applicative
). Có thể có một ý tưởng hay là cung cấp loại
vec :: forall (n :: Nat) (x :: *). x -> Vec n x
nhưng điều đó có thể hoạt động không? Để tạo n
bản sao của thứ gì đó, chúng ta cần biết n
khi chạy: chương trình phải quyết định có triển khai VNil
và dừng hoặc triển khai VCons
và tiếp tục và cần một số dữ liệu để thực hiện điều đó. Một đầu mối tốt là số phân định forall
, là tham số: nó chỉ ra rằng thông tin định lượng chỉ có sẵn cho các loại và bị xóa theo thời gian chạy.
Haskell hiện thực thi một sự trùng hợp hoàn toàn giả mạo giữa việc định lượng phụ thuộc (những gì forall
làm) và xóa bỏ thời gian chạy. Nó không không hỗ trợ một số lượng phụ thuộc nhưng không bị xóa, mà chúng ta thường gọi là pi
. Loại và thực hiện vec
nên một cái gì đó giống như
vec :: pi (n :: Nat) -> forall (x :: *). Vec n x
vec 'Zero x = VNil
vec ('Suc n) x = VCons x (vec n x)
nơi tranh cãi trong pi
-positions được viết bằng ngôn ngữ loại, nhưng các dữ liệu có sẵn tại thời gian chạy.
Vậy chúng ta phải làm gì? Chúng tôi sử dụng các trình đơn để thu thập gián tiếp ý nghĩa của việc sao chép dữ liệu cấp loại .
data SNat :: Nat -> * where
SZero :: SNat Zero
SSuc :: SNat n -> SNat (Suc n)
Bây giờ, SZero
và SSuc
làm cho dữ liệu thời gian chạy.SNat
không đồng bộ với Nat
: loại cũ có loại Nat -> *
, trong khi loại sau có loại *
, do đó, đây là lỗi loại để cố gắng làm cho chúng không đồng nhất. Có nhiều giá trị thời gian chạy trong Nat
và hệ thống loại không phân biệt chúng; có chính xác một giá trị thời gian chạy (giá trị nói) trong mỗi khác nhau SNat n
, do đó, thực tế là hệ thống kiểu không thể phân biệt chúng là bên cạnh điểm. Vấn đề là mỗi SNat n
là một loại khác nhau cho mỗi khác nhau n
và mẫu phù hợp GADT (trong đó một mẫu có thể là một ví dụ cụ thể hơn của loại GADT được biết là phù hợp) có thể tinh chỉnh kiến thức của chúng tôi về n
.
Chúng tôi bây giờ có thể viết
vec :: forall (n :: Nat). SNat n -> forall (x :: *). x -> Vec n x
vec SZero x = VNil
vec (SSuc n) x = VCons x (vec n x)
Singletons cho phép chúng ta thu hẹp khoảng cách giữa thời gian chạy và kiểu dữ liệu cấp, bằng cách khai thác hình thức duy nhất của phân tích thời gian chạy cho phép người tinh tế của loại thông tin. Nó khá hợp lý để tự hỏi liệu họ có thực sự cần thiết không, và hiện tại họ đang có, chỉ vì khoảng cách đó vẫn chưa được loại bỏ.
Không 'LoạiApplications' chủ yếu hoặc thậm chí hoàn toàn làm giảm bớt nhu cầu này? Tôi đã hack một ví dụ nhanh [ở đây] (http://ideone.com/vD4Gxp), mặc dù thừa nhận có lẽ tôi đang thiếu một số hạn chế ở đây. (Bây giờ tôi đang nghĩ về nó mặc dù, tôi không thể thực sự nghĩ ra một cách để làm điều đó mà không có một loại lớp như bạn đã làm ở đây) – Cubic
@pigworker: Rất cám ơn! Bạn viết: "mặc dù" Zero và 'Suc tồn tại ở cấp độ loại, nó là vô ích để gọi họ là' loại 'và những người hiện đang làm điều đó nên desist "Làm thế nào bạn sẽ gọi' Zero và 'Suc? Tôi nghĩ tên tốt luôn hữu ích. – Jogger
@Cubic Lớp loại nào? Và không, TypeApplications liên quan đến * một khác biệt * không liên quan: rõ ràng so với ngầm định. Đó là một sự phân biệt hoàn toàn riêng biệt so với loại so với thời gian chạy. Google "Milinc's Coincidence". – pigworker