Câu trả lời ngắn gọn là suy luận kiểu không phải lúc nào cũng hoạt động với các loại xếp hạng cao hơn. Trong trường hợp này, nó là không thể suy ra các loại (.)
, nhưng nó gõ kiểm tra nếu chúng ta thêm một loại chú thích rõ ràng:
> :m + Control.Monad.ST
> :set -XRankNTypes
> :t (((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True
(((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True :: Bool
Vấn đề tương tự cũng xảy ra với ví dụ đầu tiên của bạn, nếu chúng ta thay thế ($)
với riêng của chúng tôi phiên bản:
> let app f x = f x
> :t runST `app` (return `app` True)
<interactive>:1:14:
Couldn't match expected type `forall s. ST s t0'
with actual type `m0 t10'
Expected type: t10 -> forall s. ST s t0
Actual type: t10 -> m0 t10
In the first argument of `app', namely `return'
In the second argument of `app', namely `(return `app` True)'
một lần nữa, điều này có thể được giải quyết bằng cách thêm loại chú thích:
> :t (app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True)
(app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True) :: Bool
gì đang xảy ra ở đây là rằng có một quy tắc nhập đặc biệt trong GHC 7 chỉ áp dụng cho toán tử chuẩn ($)
. Simon Peyton-Jones giải thích hành vi này trong a reply on the GHC users mailing list:
Đây là một ví dụ động cơ thúc đẩy cho loại suy luận rằng có thể đối phó với loại impredicative. Xem xét các loại ($)
:
($) :: forall p q. (p -> q) -> p -> q
Trong ví dụ chúng ta cần phải nhanh chóng p
với (forall s. ST s a)
, và đó là những gì polymorphism impredicative có nghĩa là: instantiating một loại biến với một kiểu đa hình .
Đáng buồn thay, tôi biết không có hệ thống phức tạp hợp lý nào có thể đánh máy [điều này] chưa được thanh toán. Có rất nhiều hệ thống phức tạp và tôi có là đồng tác giả trên các giấy tờ trên ít nhất hai, nhưng tất cả đều là Quá Jolly Phức tạp để sống trong GHC. Chúng tôi đã triển khai loại boxy, nhưng tôi đã loại bỏ nó khi triển khai trình kiểm tra lỗi mới. Không ai hiểu nó.
Tuy nhiên, người ta thường xuyên viết
runST $ do ...
rằng trong GHC 7 Tôi thực hiện một quy tắc gõ đặc biệt, chỉ dành riêng cho sử dụng ghi vào của ($)
. Chỉ cần nghĩ về (f $ x)
dưới dạng một dạng cú pháp mới , với quy tắc nhập rõ ràng và bạn không thể đi.
Ví dụ thứ hai của bạn không thành công vì không có quy tắc như vậy cho (.)
.
Nếu '($)' có thể được cung cấp một chữ ký được đánh dấu là '($): forall (a: *) (b: a -> *). ((x: a) -> b x) -> (x: a) -> b x' nó sẽ hoạt động mà không có thủ thuật GHC, và tương tự cho '(.)'. – danr