Saya tidak melihat versi sintaksis yang diterbitkan yang tanda tangannya untuk sugarSym
menggunakan 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 fi
tanda 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
, sig
dan f
, dan dari tipe-tipe tersebut kita harus dapat mengikuti dependensi fungsional untuk mendisambiguasikan semua tipe lain yang digunakan dalam konteks, yaitu sup
dan fi
. Dan memang, f -> internal
ketergantungan fungsional dalam SyntacticN
menggunakan kita f
untuk disambiguasi kita fi
, dan kemudian f -> sig sym
ketergantungan fungsional dalam ApplySym
menggunakan kita yang baru-disambiguasi fi
untuk disambiguasi sup
(dan sig
, yang sudah non-ambigu). Jadi itu menjelaskan mengapa sugarSym
tidak memerlukan AllowAmbiguousTypes
ekstensi.
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 fi
dan 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 fi
juga 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 fi
atau 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) => f
dan tidak melibatkan variabel tipe ambigu?