Saya tidak melihat versi sintaksis yang diterbitkan yang tanda tangannya untuk sugarSymmenggunakan nama-nama jenis yang tepat, jadi saya akan menggunakan cabang pengembangan di commit 8cfd02 ^ , versi terakhir yang masih menggunakan nama-nama itu.
Jadi, mengapa GHC mengeluh tentang fitanda tangan tipe Anda tetapi bukan untuk sugarSym? Dokumentasi yang Anda tautkan menjelaskan bahwa suatu tipe ambigu jika tidak muncul di sebelah kanan kendala, kecuali jika kendala tersebut menggunakan dependensi fungsional untuk menyimpulkan tipe yang ambigu dari tipe non-ambigu lainnya. Jadi mari kita bandingkan konteks kedua fungsi dan mencari dependensi fungsional.
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
Jadi untuk sugarSym, tipe-tipe non-ambigu adalah sub, sigdan f, dan dari tipe-tipe tersebut kita harus dapat mengikuti dependensi fungsional untuk mendisambiguasikan semua tipe lain yang digunakan dalam konteks, yaitu supdan fi. Dan memang, f -> internalketergantungan fungsional dalam SyntacticNmenggunakan kita funtuk disambiguasi kita fi, dan kemudian f -> sig symketergantungan fungsional dalam ApplySymmenggunakan kita yang baru-disambiguasi fiuntuk disambiguasi sup(dan sig, yang sudah non-ambigu). Jadi itu menjelaskan mengapa sugarSymtidak memerlukan AllowAmbiguousTypesekstensi.
Sekarang mari kita lihat sugar. Hal pertama yang saya perhatikan adalah bahwa kompiler tidak mengeluh tentang tipe yang ambigu, melainkan tentang tumpang tindih contoh:
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
Jadi, jika saya membaca ini dengan benar, bukan karena GHC berpikir bahwa tipe Anda ambigu, melainkan, saat memeriksa apakah tipe Anda ambigu, GHC menghadapi masalah yang berbeda dan terpisah. Ini kemudian memberi tahu Anda bahwa jika Anda memberi tahu GHC untuk tidak melakukan pemeriksaan ambiguitas, itu tidak akan menemui masalah yang terpisah. Ini menjelaskan mengapa mengaktifkan AllowAmbiguousTypes memungkinkan kode Anda dikompilasi.
Namun, masalah dengan instance yang tumpang tindih tetap ada. Dua contoh yang terdaftar oleh GHC ( SyntacticN f fidan SyntacticN (a -> f) ...) saling tumpang tindih. Anehnya, sepertinya yang pertama harus tumpang tindih dengan contoh lain, yang mencurigakan. Dan apa [overlap ok]artinya?
Saya curiga Syntactic dikompilasi dengan OverlappingInstances. Dan melihat kodenya , memang begitu.
Bereksperimen sedikit, tampaknya GHC baik-baik saja dengan contoh yang tumpang tindih ketika jelas bahwa yang satu lebih ketat daripada yang lain:
{-# 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])
Tetapi GHC tidak baik dengan kasus yang tumpang tindih ketika tidak ada yang jelas lebih cocok dari yang lain:
{-# 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])
Jenis tanda tangan Anda gunakan SyntacticN (a -> (a -> b) -> b) fi, dan tidak SyntacticN f fijuga SyntacticN (a -> f) (AST sym (Full ia) -> fi)lebih cocok daripada yang lain. Jika saya mengubah bagian dari tanda tangan tipe Anda menjadi SyntacticN a fiatau SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi), GHC tidak lagi mengeluh tentang tumpang tindih.
Jika saya adalah Anda, saya akan melihat definisi dari dua contoh yang mungkin dan menentukan apakah salah satu dari dua implementasi itu adalah yang Anda inginkan.
sugarSym Let, yang merupakan(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => fdan tidak melibatkan variabel tipe ambigu?