Michael đã đưa ra một lời giải thích tốt trong bài viết trên blog của mình, nhưng tôi sẽ cố gắng minh họa nó bằng một ví dụ (contrived, nhưng tương đối nhỏ).
Chúng ta cần mở rộng sau:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
Hãy xác định một lớp học đó là đơn giản hơn CanFilter
, chỉ với một tham số. Tôi đang xác định hai bản sao của lớp học, bởi vì tôi muốn chứng minh sự khác biệt về hành vi giữa hai trường hợp:
class Twice1 f where
twice1 :: f -> f
class Twice2 f where
twice2 :: f -> f
Bây giờ, hãy xác định một cá thể cho mỗi lớp. Đối với Twice1
, chúng tôi sửa các biến loại giống nhau trực tiếp và đối với Twice2
, chúng tôi cho phép chúng khác nhau, nhưng thêm ràng buộc bình đẳng.
instance Twice1 (a -> a) where
twice1 f = f . f
instance (a ~ b) => Twice2 (a -> b) where
twice2 f = f . f
Để thấy sự khác biệt, chúng ta hãy xác định một chức năng quá tải như thế này:
class Example a where
transform :: Int -> a
instance Example Int where
transform n = n + 1
instance Example Char where
transform _ = 'x'
Bây giờ chúng ta đang ở một điểm mà chúng ta có thể thấy sự khác biệt. Một khi chúng ta định nghĩa
apply1 x = twice1 transform x
apply2 x = twice2 transform x
và yêu cầu GHC cho các loại suy ra, chúng tôi nhận được rằng
apply1 :: (Example a, Twice1 (Int -> a)) => Int -> a
apply2 :: Int -> Int
Tại sao vậy? Vâng, trường hợp cho Twice1
chỉ kích hoạt khi nguồn và loại mục tiêu của hàm giống nhau. Đối với transform
và bối cảnh đã cho, chúng tôi không biết điều đó. GHC sẽ chỉ áp dụng một trường hợp một khi phía bên tay phải phù hợp, vì vậy chúng tôi được trái với bối cảnh chưa được giải quyết. Nếu chúng tôi cố gắng nói apply1 0
, sẽ có lỗi loại cho biết rằng vẫn không có đủ thông tin để giải quyết quá tải. Chúng tôi phải chỉ định rõ ràng loại kết quả là Int
trong trường hợp này để vượt qua.
Tuy nhiên, trong Twice2
, phiên bản dành cho bất kỳ loại chức năng nào. GHC sẽ ngay lập tức giải quyết nó (GHC không bao giờ backtracks, vì vậy nếu một instance rõ ràng khớp, nó luôn luôn được chọn), và sau đó cố gắng thiết lập các điều kiện tiên quyết: trong trường hợp này, ràng buộc bình đẳng, sau đó buộc loại kết quả là Int
và cho phép chúng ta để giải quyết ràng buộc Example
. Chúng tôi có thể nói apply2 0
mà không cần chú thích thêm.
Vì vậy, đây là một điểm khá tinh tế về giải pháp thể hiện của GHC và ràng buộc bình đẳng ở đây giúp kiểm tra loại GHC theo cách yêu cầu người dùng ít chú thích hơn.
Cảm ơn. Tôi nghi ngờ nó như thế, nhưng ví dụ đơn giản của bạn làm mọi việc rõ ràng hơn nhiều. – Clinton