Nie widzę żadnej opublikowanej wersji syntactic, której podpis dla sugarSym
używa tych dokładnych nazw typów, więc będę używał gałęzi programistycznej w commit 8cfd02 ^ , ostatniej wersji, która nadal używała tych nazw.
Dlaczego więc GHC narzeka na fi
podpis w twoim typie, ale nie na ten sugarSym
? Dokumentacja, z którą się łączysz, wyjaśnia, że typ jest niejednoznaczny, jeśli nie wydaje się po prawej stronie ograniczenia, chyba że ograniczenie wykorzystuje zależności funkcjonalne do wnioskowania o niejednoznacznym typie z innych niejednoznacznych typów. Porównajmy więc konteksty dwóch funkcji i poszukaj zależności funkcjonalnych.
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
Tak dla sugarSym
, nie-niejednoznaczne typy są sub
, sig
i f
, i od tych, powinniśmy być w stanie śledzić zależności funkcyjnych w celu disambiguate wszystkie inne typy używanych w kontekście, a mianowicie sup
a fi
. I faktycznie, f -> internal
funkcjonalna zależność SyntacticN
używa naszego f
do ujednoznacznienia naszego fi
, a następnie f -> sig sym
funkcjonalna zależność w ApplySym
używa naszego nowego ujednoznacznienia fi
do ujednoznacznienia sup
(i sig
, co już było niejednoznaczne). To wyjaśnia, dlaczego sugarSym
nie wymaga AllowAmbiguousTypes
rozszerzenia.
Spójrzmy teraz sugar
. Pierwszą rzeczą, którą zauważam, jest to, że kompilator nie narzeka na dwuznaczny typ, ale raczej na nakładające się wystąpienia:
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
Więc jeśli dobrze to rozumiem, to nie jest tak, że GHC myśli, że twoje typy są niejednoznaczne, ale raczej, że podczas sprawdzania, czy twoje typy są niejednoznaczne, GHC napotkał inny, osobny problem. Mówi ci wtedy, że gdybyś powiedział GHC, aby nie przeprowadzał kontroli niejednoznaczności, nie napotkałby tego osobnego problemu. To wyjaśnia, dlaczego włączenie AllowAmbiguousTypes umożliwia kompilację kodu.
Pozostaje jednak problem z nakładającymi się instancjami. Dwa wystąpienia wymienione przez GHC ( SyntacticN f fi
i SyntacticN (a -> f) ...
) pokrywają się ze sobą. O dziwo, wydaje się, że pierwszy z nich powinien nakładać się na każdy inny podejrzany przypadek. A co to [overlap ok]
znaczy
Podejrzewam, że Syntactic jest kompilowany z OverlappingInstances. I patrząc na kod , rzeczywiście tak jest.
Trochę eksperymentując, wydaje się, że GHC jest w porządku z nakładającymi się instancjami, gdy jest jasne, że jedna jest bardziej ogólna niż druga:
{-# 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])
Ale GHC nie jest w porządku z nakładającymi się instancjami, gdy żadna z nich nie jest wyraźnie lepsza od drugiej:
{-# 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])
Podpis twojego typu używa SyntacticN (a -> (a -> b) -> b) fi
, i ani SyntacticN f fi
nie SyntacticN (a -> f) (AST sym (Full ia) -> fi)
jest lepiej dopasowany niż drugi. Jeśli zmienię tę część twojego podpisu na SyntacticN a fi
lub SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC nie będzie już narzekać na nakładanie się.
Gdybym był tobą, spojrzałbym na definicję tych dwóch możliwych instancji i ustalił, czy jedna z tych dwóch implementacji jest tą, której chcesz.
sugarSym Let
, który jest(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
zmiennymi typu niejednoznacznego i nie obejmuje tych zmiennych?