Non vedo alcuna versione pubblicata di sintattica la cui firma per sugarSymutilizza quei nomi esatti di tipi, quindi userò il ramo di sviluppo su commit 8cfd02 ^ , l'ultima versione che ancora utilizzava quei nomi.
Quindi, perché GHC si lamenta della fifirma del tuo tipo ma non di quella sugarSym? La documentazione a cui hai collegato spiega che un tipo è ambiguo se non appare a destra del vincolo, a meno che il vincolo non stia usando dipendenze funzionali per inferire il tipo altrimenti ambiguo da altri tipi non ambigui. Quindi confrontiamo i contesti delle due funzioni e cerchiamo dipendenze funzionali.
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
Quindi sugarSym, i tipi non ambigui sono sub, sige f, e da quelli che dovremmo essere in grado di seguire le dipendenze funzionali al fine di chiarire tutti gli altri tipi utilizzati nel contesto, vale a dire supe fi. E in effetti, la f -> internaldipendenza funzionale in SyntacticNusa il nostro fper chiarire le nostre fi, e in seguito la f -> sig symdipendenza funzionale in ApplySymusa il nostro appena chiarito fiper chiarire sup(e sig, che era già non ambiguo). Questo spiega perché sugarSymnon richiede l' AllowAmbiguousTypesestensione.
Vediamo ora sugar. La prima cosa che noto è che il compilatore non si lamenta di un tipo ambiguo, ma piuttosto di istanze sovrapposte:
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
Quindi, se sto leggendo bene, non è che GHC pensi che i tuoi tipi siano ambigui, ma piuttosto, mentre controlla se i tuoi tipi sono ambigui, GHC ha riscontrato un problema diverso e separato. Ti sta quindi dicendo che se avessi detto a GHC di non eseguire il controllo dell'ambiguità, non avrebbe riscontrato quel problema separato. Questo spiega perché abilitando AllowAmbiguousTypes è possibile compilare il codice.
Tuttavia, il problema con le istanze sovrapposte rimane. Le due istanze elencate da GHC ( SyntacticN f fie SyntacticN (a -> f) ...) si sovrappongono. Stranamente, sembra che il primo di questi dovrebbe sovrapporsi a qualsiasi altra istanza, il che è sospetto. E cosa [overlap ok]significa?
Sospetto che Syntactic sia compilato con OverlappingInstances. E guardando il codice , in effetti lo fa.
Sperimentando un po ', sembra che GHC stia bene con casi sovrapposti quando è chiaro che uno è strettamente più generale dell'altro:
{-# 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])
Ma GHC non va bene con casi sovrapposti in cui nessuno dei due è chiaramente migliore degli altri:
{-# 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])
La tua firma di tipo utilizza SyntacticN (a -> (a -> b) -> b) fie né SyntacticN f finé SyntacticN (a -> f) (AST sym (Full ia) -> fi)si adatta meglio dell'altra. Se cambio quella parte della firma del tuo tipo in SyntacticN a fio SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi), GHC non si lamenta più della sovrapposizione.
Se fossi in te, esaminerei la definizione di quei due possibili casi e determinerei se una di quelle due implementazioni è quella che desideri.
sugarSym Let, che è(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => fe non coinvolge variabili di tipo ambigue?