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ý sugarSymsử 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ề fichữ 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 sugarSymcác loại không mơ hồ sub, sigvà 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à supvà fi. Và thực tế, sự f -> internalphụ thuộc chức năng trong việc SyntacticNsử dụng của chúng tôi fđể định hướng của chúng tôi fi, và sau đó sự f -> sig symphụ thuộc chức năng trong việc ApplySymsử 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 sugarSymkhông yêu cầu AllowAmbiguousTypesgia 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 fivà 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 ficũ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 fihoặ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) => fvà không liên quan đến các biến loại mơ hồ?