Je ne vois aucune version publiée de syntaxique dont la signature sugarSym
utilise ces noms de type exacts, donc j'utiliserai la branche de développement à commit 8cfd02 ^ , la dernière version qui utilisait toujours ces noms.
Alors, pourquoi GHC se plaint-il de la fi
signature de votre type mais pas de celle pour sugarSym
? La documentation à laquelle vous avez lié explique qu'un type est ambigu s'il n'apparaît pas à droite de la contrainte, sauf si la contrainte utilise des dépendances fonctionnelles pour déduire le type par ailleurs ambigu à partir d'autres types non ambigus. Comparons donc les contextes des deux fonctions et recherchons les dépendances fonctionnelles.
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
Donc pour sugarSym
, les types non ambigus sont sub
, sig
et f
, à partir de ceux-ci, nous devrions pouvoir suivre les dépendances fonctionnelles afin de lever l'ambiguïté de tous les autres types utilisés dans le contexte, à savoir sup
et fi
. Et en effet, la f -> internal
dépendance fonctionnelle dans SyntacticN
utilise notre f
pour lever l'ambiguïté fi
, et par la suite la f -> sig sym
dépendance fonctionnelle dans ApplySym
utilise notre nouvellement désambiguïsé fi
pour lever l' ambiguïté sup
(et sig
, ce qui était déjà non ambigu). Cela explique pourquoi sugarSym
ne nécessite pas l' AllowAmbiguousTypes
extension.
Regardons maintenant sugar
. La première chose que je remarque est que le compilateur ne se plaint pas d'un type ambigu, mais plutôt de superpositions d'instances:
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
Donc, si je lis bien, ce n'est pas que GHC pense que vos types sont ambigus, mais plutôt, qu'en vérifiant si vos types sont ambigus, GHC a rencontré un problème différent et distinct. Il vous indique ensuite que si vous aviez demandé à GHC de ne pas effectuer la vérification d'ambiguïté, il n'aurait pas rencontré ce problème distinct. Cela explique pourquoi l'activation de AllowAmbiguousTypes permet à votre code de se compiler.
Cependant, le problème avec les instances qui se chevauchent reste. Les deux instances répertoriées par GHC ( SyntacticN f fi
et SyntacticN (a -> f) ...
) se chevauchent. Curieusement, il semble que le premier d'entre eux devrait se chevaucher avec toute autre instance, ce qui est suspect. Et qu'est-ce que cela [overlap ok]
signifie?
Je soupçonne que Syntactic est compilé avec OverlappingInstances. Et en regardant le code , c'est le cas.
En expérimentant un peu, il semble que GHC accepte les instances qui se chevauchent lorsqu'il est clair que l'une est strictement plus générale que l'autre:
{-# 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])
Mais GHC n'est pas d'accord avec des instances qui se chevauchent lorsque ni l'une ni l'autre n'est clairement mieux adaptée que l'autre:
{-# 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])
Votre signature de type utilise SyntacticN (a -> (a -> b) -> b) fi
, et ni l'un SyntacticN f fi
ni l' autre ne SyntacticN (a -> f) (AST sym (Full ia) -> fi)
convient mieux que l'autre. Si je change cette partie de votre signature de type en SyntacticN a fi
ou SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC ne se plaint plus du chevauchement.
Si j'étais vous, je regarderais la définition de ces deux instances possibles et déterminer si l'une de ces deux implémentations est celle que vous voulez.
sugarSym Let
, qui est(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
et n'implique pas de variables de type ambiguë?