Khi giải thích bộ kết hợp Y trong ngữ cảnh của Haskell, người ta thường lưu ý rằng việc triển khai thẳng sẽ không kiểm tra kiểu trong Haskell vì loại đệ quy của nó.
Ví dụ: từ Rosettacode :
The obvious definition of the Y combinator in Haskell canot be used
because it contains an infinite recursive type (a = a -> b). Defining
a data type (Mu) allows this recursion to be broken.
newtype Mu a = Roll { unroll :: Mu a -> a }
fix :: (a -> a) -> a
fix = \f -> (\x -> f (unroll x x)) $ Roll (\x -> f (unroll x x))
Và thực tế, định nghĩa rõ ràng của Viking không gõ kiểm tra:
λ> let fix f g = (\x -> \a -> f (x x) a) (\x -> \a -> f (x x) a) g
<interactive>:10:33:
Occurs check: cannot construct the infinite type:
t2 = t2 -> t0 -> t1
Expected type: t2 -> t0 -> t1
Actual type: (t2 -> t0 -> t1) -> t0 -> t1
In the first argument of `x', namely `x'
In the first argument of `f', namely `(x x)'
In the expression: f (x x) a
<interactive>:10:57:
Occurs check: cannot construct the infinite type:
t2 = t2 -> t0 -> t1
In the first argument of `x', namely `x'
In the first argument of `f', namely `(x x)'
In the expression: f (x x) a
(0.01 secs, 1033328 bytes)
Giới hạn tương tự tồn tại trong Ocaml:
utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
Error: This expression has type 'a -> 'b but an expression was expected of type 'a
The type variable 'a occurs inside 'a -> 'b
Tuy nhiên, trong Ocaml, người ta có thể cho phép các loại đệ quy bằng cách chuyển qua công -rectypes
tắc:
-rectypes
Allow arbitrary recursive types during type-checking. By default, only recursive
types where the recursion goes through an object type are supported.
Bằng cách sử dụng -rectypes
, mọi thứ hoạt động:
utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
utop # let fact_improver partial n = if n = 0 then 1 else n*partial (n-1);;
val fact_improver : (int -> int) -> int -> int = <fun>
utop # (fix fact_improver) 5;;
- : int = 120
Tò mò về hệ thống loại và suy luận kiểu, điều này đặt ra một số câu hỏi tôi vẫn không thể trả lời.
- Đầu tiên, làm thế nào để trình kiểm tra loại đến với loại
t2 = t2 -> t0 -> t1
? Đã đưa ra loại đó, tôi đoán vấn đề là loại (t2
) đề cập đến chính nó ở phía bên phải? - Thứ hai, và có lẽ thú vị nhất, lý do nào khiến các hệ thống loại Haskell / Ocaml không cho phép điều này? Tôi đoán có là một lý do chính đáng kể từ Ocaml cũng sẽ không cho phép nó theo mặc định ngay cả khi nó có thể đối phó với các loại đệ quy nếu có
-rectypes
chuyển đổi.
Nếu đây là những chủ đề thực sự lớn, tôi sẽ đánh giá cao con trỏ đến các tài liệu liên quan.