我没有看到任何已发布的语法版本的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
So for sugarSym, the non-ambiguous types are sub, sig and f, and from those we should be able to follow functional dependencies in order to disambiguate all the other types used in the context, namely sup and fi. And indeed, the f -> internal functional dependency in SyntacticN uses our f to disambiguate our fi, and thereafter the f -> sig sym functional dependency in ApplySym uses our newly-disambiguated fi to disambiguate sup (and sig, which was already non-ambiguous). So that explains why sugarSym doesn't require the AllowAmbiguousTypes extension.
现在让我们来看看糖。我注意到的第一件事是,编译器并没有抱怨模棱两可的类型,而是抱怨重叠的实例:
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不要执行歧义检查,它就不会遇到这个单独的问题。这解释了为什么启用allowambiguousttypes可以让你的代码编译。
但是,实例重叠的问题仍然存在。GHC列出的两个实例(SyntacticN f fi和SyntacticN (a -> f)…)确实彼此重叠。奇怪的是,这其中的第一个似乎与其他任何实例都有重叠,这是可疑的。重叠ok是什么意思?
我怀疑Syntactic是用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将不再抱怨重叠。
如果我是您,我会查看这两个可能实例的定义,并确定这两个实现中是否有一个是您想要的。