Trường hợp (1) không thành công nhưng (2) không nhỏ; vì loại từ đồng nghĩa (type ExampleOfATypeSynonym = ...
) không được phép vào tờ khai ví dụ, nhưng họ được cho phép trong những hạn chế, bất kỳ tình huống mà bạn chỉ có một dụ như thế này:
-- (1)
class Foo a
type Bla =()
instance Foo Bla
... có thể được chuyển đổi thành:
-- (2)
class Foo a
type Bla =()
instance (a ~ Bla) => Foo a
Lý do duy nhất tại sao (1) không thành công vì loại từ đồng nghĩa không được phép trong khai báo cá thể, và đó là do loại từ đồng nghĩa giống như chức năng loại: chúng cung cấp ánh xạ một chiều từ tên loại đến tên loại, vì vậy nếu bạn có type B = A
và instance Foo B
, không rõ ràng rằng một thể hiện của Foo A
được tạo ra để thay thế. Quy tắc tồn tại để bạn phải viết instance Foo A
thay vào đó để làm rõ rằng rằng là loại thực sự nhận được cá thể.
Việc sử dụng loại gia đình không liên quan trong ngữ cảnh này, bởi vì vấn đề là bạn đang sử dụng loại đồng nghĩa loại, loại NameRecord
. Bạn cũng phải ghi nhớ rằng nếu từ đồng nghĩa loại được loại bỏ và thay thế bằng FieldOf Name
trực tiếp, biên dịch sẽ vẫn không thành công; đó là vì "loại gia đình" chỉ là phiên bản nâng cao của các từ đồng nghĩa loại, do đó, FieldOf Name
cũng là "loại từ đồng nghĩa" cho Name :> Text
trong ngữ cảnh này. Bạn phải sử dụng một họ dữ liệu và một cá thể dữ liệu thay vào đó để có được một liên kết "hai chiều".
Thông tin thêm về họ dữ liệu có thể được tìm thấy trong GHC documentation.
Tôi nghĩ bạn có nghĩa là "... ở đâu (2) không thành công nhưng (1) không ..."
Hãy tưởng tượng rằng chúng tôi có một kiểu lớp như vậy:
class Foo a where
foo :: a
Bây giờ, bạn có thể viết các trường hợp như vậy:.
instance Foo Int where
foo = 0
instance Foo Float where
foo = 0
main :: IO()
main = print (foo :: Float)
này hoạt động như một mong chờ Tuy nhiên, nếu bạn chuyển đổi mã này thành:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
class Foo a where
foo :: a
instance (a ~ Int) => Foo a where
foo = 0
instance (a ~ Float) => Foo a where
foo = 0
main :: IO()
main = print (foo :: Float)
Nó không biên dịch; nó hiển thị lỗi:
test.hs:5:10:
Duplicate instance declarations:
instance a ~ Int => Foo a -- Defined at test.hs:5:10-27
instance a ~ Float => Foo a -- Defined at test.hs:8:10-29
Vì vậy, đây là ví dụ bạn hy vọng đang tìm kiếm. Bây giờ, điều này chỉ xảy ra nếu có nhiều hơn một phiên bản của Foo
sử dụng thủ thuật này. Tại sao vậy?
Khi GHC phân giải các loại lớp, nó chỉ nhìn vào đầu khai báo cá thể; nghĩa là nó bỏ qua mọi thứ trước =>
. Khi nó đã chọn một thể hiện, nó "cam kết" với nó, và kiểm tra các ràng buộc trước =>
để xem chúng có đúng không. Vì vậy, lúc đầu nó thấy hai trường hợp:
instance Foo a where ...
instance Foo a where ...
Nó rõ ràng là không thể để quyết định dụ để sử dụng dựa trên các thông tin này một mình.
Không quan tâm, bạn có biết liệu phương pháp giải quyết các loại lớp của GHC có phải là một quyết định thiết kế rõ ràng không? Và nếu vậy, lý do cho nó? (Hoặc có thể thay thế quá phức tạp để thực hiện một cách rõ ràng?) – huon
Lý do cho nó là Báo cáo Haskell yêu cầu nó hoạt động như thế này. Lý do cho ** rằng ** là nếu nó không hoạt động như thế này, thì phải có một thuật toán đưa ra một heuristic cho "một loại phù hợp với một ràng buộc như thế nào;" bạn phải tranh luận "Vâng, loại này phù hợp với cá thể lớp này, nhưng trường hợp khác phù hợp hơn nhiều vì {ít ràng buộc, khoảng cách giảm ngắn hơn, ...}." Nó có thể có thể phát triển như một heuristic, nhưng nó sẽ phá vỡ Open World Assumption là một khái niệm quan trọng khi nói đến các lớp loại. – dflemstr
Hãy tưởng tượng 'instance string ~ a => Foo a' và' instance a ~ [b] => Foo a'. Đó là một ví dụ về các trường hợp bạn cần một thuật toán để giải quyết 'Foo [Char]'. – dflemstr