Tôi không thấy bất kỳ phiên bản cú pháp được xuất bản nào có chữ ký sugarSym
sử dụng các tên loại chính xác đó, vì vậy tôi sẽ sử dụng nhánh phát triển tại commit 8cfd02 ^ , phiên bản cuối cùng vẫn sử dụng các tên đó.
Vì vậy, tại sao GHC phàn nàn về fi
chữ ký trong loại của bạn mà không phải là chữ ký cho sugarSym
? Tài liệu bạn đã liên kết để giải thích rằng một loại không rõ ràng nếu nó không xuất hiện bên phải của ràng buộc, trừ khi ràng buộc đó là sử dụng các phụ thuộc chức năng để suy ra loại không rõ ràng khác với các loại không mơ hồ khác. Vì vậy, hãy so sánh bối cảnh của hai chức năng và tìm kiếm các phụ thuộc chức năng.
class ApplySym sig f sym | sig sym -> f, f -> sig sym
class SyntacticN f internal | f -> internal
sugarSym :: ( sub :<: AST sup
, ApplySym sig fi sup
, SyntacticN f fi
)
=> sub sig -> f
share :: ( Let :<: sup
, sup ~ Domain b
, sup ~ Domain a
, Syntactic a
, Syntactic b
, Syntactic (a -> b)
, SyntacticN (a -> (a -> b) -> b) fi
)
=> a -> (a -> b) -> b
Vì vậy, đối với sugarSym
các loại không mơ hồ sub
, sig
và f
, từ các loại chúng ta sẽ có thể tuân theo các phụ thuộc chức năng để phân tán tất cả các loại khác được sử dụng trong ngữ cảnh, cụ thể là sup
và fi
. Và thực tế, sự f -> internal
phụ thuộc chức năng trong việc SyntacticN
sử dụng của chúng tôi f
để định hướng của chúng tôi fi
, và sau đó sự f -> sig sym
phụ thuộc chức năng trong việc ApplySym
sử dụng chúng tôi mới định hướng fi
để định hướng sup
(và sig
, điều này đã không mơ hồ). Vì vậy, điều đó giải thích tại sao sugarSym
không yêu cầu AllowAmbiguousTypes
gia hạn.
Bây giờ chúng ta hãy nhìn vào sugar
. Điều đầu tiên tôi nhận thấy là trình biên dịch không phàn nàn về một kiểu mơ hồ, mà là về các trường hợp chồng chéo:
Overlapping instances for SyntacticN b fi
arising from the ambiguity check for ‘share’
Matching givens (or their superclasses):
(SyntacticN (a -> (a -> b) -> b) fi1)
Matching instances:
instance [overlap ok] (Syntactic f, Domain f ~ sym,
fi ~ AST sym (Full (Internal f))) =>
SyntacticN f fi
-- Defined in ‘Data.Syntactic.Sugar’
instance [overlap ok] (Syntactic a, Domain a ~ sym,
ia ~ Internal a, SyntacticN f fi) =>
SyntacticN (a -> f) (AST sym (Full ia) -> fi)
-- Defined in ‘Data.Syntactic.Sugar’
(The choice depends on the instantiation of ‘b, fi’)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
Vì vậy, nếu tôi đọc đúng, không phải GHC nghĩ rằng các loại của bạn không rõ ràng, mà là trong khi kiểm tra xem các loại của bạn có mơ hồ hay không, GHC gặp phải một vấn đề riêng biệt khác. Sau đó, bạn sẽ nói với bạn rằng nếu bạn nói với GHC không thực hiện kiểm tra sự mơ hồ, thì nó sẽ không gặp phải vấn đề riêng biệt đó. Điều này giải thích tại sao việc kích hoạt AllowAmbiguptTypes cho phép mã của bạn biên dịch.
Tuy nhiên, vấn đề với các trường hợp chồng chéo vẫn còn. Hai trường hợp được liệt kê bởi GHC ( SyntacticN f fi
và SyntacticN (a -> f) ...
) trùng lặp với nhau. Thật kỳ lạ, có vẻ như lần đầu tiên trong số này sẽ trùng lặp với bất kỳ trường hợp nào khác, điều đáng ngờ. Và nó [overlap ok]
có nghĩa là gì?
Tôi nghi ngờ rằng Syntactic được biên dịch với OverlapsInstances. Và nhìn vào mã , thực sự nó làm.
Thử nghiệm một chút, có vẻ như GHC vẫn ổn với các trường hợp chồng chéo khi rõ ràng rằng cái này hoàn toàn tổng quát hơn cái kia:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Foo a where
whichOne :: a -> String
instance Foo a where
whichOne _ = "a"
instance Foo [a] where
whichOne _ = "[a]"
-- |
-- >>> main
-- [a]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])
Nhưng GHC không ổn với các trường hợp chồng chéo khi rõ ràng không phù hợp hơn các trường hợp khác:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Foo a where
whichOne :: a -> String
instance Foo (f Int) where -- this is the line which changed
whichOne _ = "f Int"
instance Foo [a] where
whichOne _ = "[a]"
-- |
-- >>> main
-- Error: Overlapping instances for Foo [Int]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])
Chữ ký loại của bạn sử dụng SyntacticN (a -> (a -> b) -> b) fi
, và không phải SyntacticN f fi
cũng không SyntacticN (a -> f) (AST sym (Full ia) -> fi)
phù hợp hơn loại kia. Nếu tôi thay đổi một phần chữ ký loại của bạn thành SyntacticN a fi
hoặc SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC không còn phàn nàn về sự chồng chéo.
Nếu tôi là bạn, tôi sẽ xem xét định nghĩa của hai trường hợp có thể đó và xác định xem một trong hai triển khai đó có phải là điều bạn muốn không.
sugarSym Let
, đó là(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
và không liên quan đến các biến loại mơ hồ?