Я не вижу ни одной опубликованной версии синтаксиса, подпись которой sugarSym
использует эти точные имена типов, поэтому я буду использовать ветку разработки на коммите 8cfd02 ^ , последней версии, которая все еще использовала эти имена.
Итак, почему GHC жалуется на fi
подпись в вашем типе, а не на подпись sugarSym
? В документации, на которую вы ссылаетесь, объясняется, что тип является неоднозначным, если он не отображается справа от ограничения, если только ограничение не использует функциональные зависимости, чтобы вывести неоднозначный в противном случае тип из других не неоднозначных типов. Итак, давайте сравним контексты двух функций и поищем функциональные зависимости.
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
Так sugarSym
, не-неоднозначные типы sub
, sig
и f
, и от тех , что мы должны быть в состоянии следовать функциональным зависимостям, чтобы неоднозначность всех других типов , используемых в контексте, а именно sup
и fi
. И действительно, f -> internal
функциональная зависимость в SyntacticN
использует нашу, f
чтобы устранить неоднозначность fi
, и после этого f -> sig sym
функциональная зависимость ApplySym
использует нашу недавно устраненную неоднозначность fi
для устранения неоднозначности sup
(и sig
, которая уже была неоднозначной). Это объясняет, почему sugarSym
не требуется AllowAmbiguousTypes
расширение.
Давайте теперь посмотрим sugar
. Первое, что я заметил, это то, что компилятор не жалуется на неоднозначный тип, а на перекрывающиеся экземпляры:
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
Поэтому, если я правильно понял, это не значит, что GHC думает, что ваши типы неоднозначны, а скорее, что при проверке, являются ли ваши типы неоднозначными, GHC столкнулся с другой, отдельной проблемой. Затем он говорит вам, что если бы вы сказали GHC не выполнять проверку неоднозначности, он бы не столкнулся с этой отдельной проблемой. Это объясняет, почему включение AllowAmbiguousTypes позволяет вашему коду компилироваться.
Однако проблема с перекрывающимися экземплярами остается. Два экземпляра, перечисленные GHC ( SyntacticN f fi
и SyntacticN (a -> f) ...
), перекрывают друг друга. Как ни странно, первый из них должен совпадать с любым другим экземпляром, что является подозрительным. И что это [overlap ok]
значит?
Я подозреваю, что синтаксический компилируется с OverlappingInstances. И, глядя на код , это действительно так.
Немного поэкспериментировав, кажется, что GHC в порядке с перекрывающимися экземплярами, когда становится ясно, что одно строго более общее, чем другое:
{-# 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])
Но GHC не подходит для перекрывающихся случаев, когда ни один из них явно не подходит лучше, чем другой:
{-# 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])
Ваша подпись типа использует SyntacticN (a -> (a -> b) -> b) fi
, и ни то, SyntacticN f fi
ни другое не SyntacticN (a -> f) (AST sym (Full ia) -> fi)
подходит лучше, чем другие. Если я изменю эту часть подписи вашего типа на SyntacticN a fi
или SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC больше не будет жаловаться на совпадение.
На вашем месте я бы посмотрел на определение этих двух возможных экземпляров и определил, является ли одна из этих двух реализаций той, которую вы хотите.
sugarSym Let
, который включает(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
и не включает в себя переменные неоднозначного типа?