Vấn đề là predChurch
là quá đa hình được suy luận chính xác theo suy luận kiểu Hindley-Milner. Ví dụ: bạn có thể viết:
predChurch :: Church a -> Church a
predChurch = \n -> \f -> \x -> n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)
nhưng loại này không chính xác. A Church a
lấy làm đối số đầu tiên của nó là a -> a
, nhưng bạn đang vượt qua n
một hàm hai đối số, rõ ràng là lỗi loại.
Vấn đề là Church a
không mô tả đúng số nhà thờ. Số nhà thờ chỉ đơn giản là đại diện cho một số - thông số loại đó có ý nghĩa gì trên trái đất? Ví dụ:
foo :: Church Int
foo f x = f x `mod` 42
Lỗi đánh máy đó, nhưng chắc chắn không phải là số nhà thờ. Chúng ta cần phải hạn chế loại. Số của Giáo hội cần phải làm việc cho bất kỳa
, không chỉ là a
cụ thể. Định nghĩa chính xác là:
type Church = forall a. (a -> a) -> (a -> a)
Bạn cần có {-# LANGUAGE RankNTypes #-}
ở đầu tệp để bật các loại như thế này.
Bây giờ chúng ta có thể cung cấp cho các loại chữ ký, chúng tôi mong đợi:
predChurch :: Church -> Church
-- same as before
Bạn phải cho một chữ ký kiểu ở đây vì loại hạng cao hơn được không inferrable bởi Hindley-Milner.
Tuy nhiên, khi chúng tôi đi đến thực hiện subChurch
vấn đề khác được đặt ra:
Couldn't match expected type `Church'
against inferred type `(a -> a) -> a -> a'
Tôi không chắc chắn 100% lý do tại sao điều này xảy ra, tôi nghĩ rằng forall
đang được quá tự do mở ra bởi các typechecker. Nó không làm tôi ngạc nhiên; các loại xếp hạng cao hơn có thể hơi giòn vì những khó khăn mà chúng xuất hiện cho trình biên dịch.Ngoài ra, chúng tôi không nên sử dụng một số type
để trừu tượng trừu tượng, chúng tôi sẽ sử dụng newtype
(điều này giúp chúng tôi linh hoạt hơn trong định nghĩa, giúp trình biên dịch có đánh máy và đánh dấu những nơi chúng tôi sử dụng việc triển khai trừu tượng) :
newtype Church = Church { unChurch :: forall a. (a -> a) -> (a -> a) }
và chúng ta phải sửa đổi predChurch
để cuộn và cuộn khi cần thiết:
predChurch = \n -> Church $
\f -> \x -> unChurch n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)
Cùng với subChurch
:
subChurch = \m -> \n -> unChurch n predChurch m
Nhưng chúng tôi không cần chữ ký kiểu nữa - có đủ thông tin trong cuộn/bỏ chọn để suy ra các loại một lần nữa.
Tôi luôn khuyên bạn nên newtype
giây khi tạo một trừu tượng mới. Thường xuyên type
từ đồng nghĩa khá hiếm trong mã của tôi.
Bạn nên khai báo kiểu 'loại Church a = (a -> a) -> a -> a'. Của nó sạch hơn, không khác nhau. – alternative
Cũng lưu ý rằng nó giúp một tấn để viết ra các chữ ký loại. Nó sẽ cho bạn biết chính xác nơi mà vấn đề là ... – alternative
Tôi đã kết thúc loại bỏ các chữ ký loại, để xem nếu ghci có thể suy ra chúng đúng cách, và hy vọng thoát khỏi lỗi (lỗi đã không thay đổi) ... cũng, tôi thích các dấu ngoặc đơn xung quanh loại. Nó làm cho nó nổi bật hơn với tôi – Probie