我没有看到任何发布的语法签名的版本sugarSym
使用那些确切的类型名称,因此我将在commit 8cfd02 ^(最后一个仍使用这些名称的版本)处使用development分支。
那么,为什么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
又不涉及模棱两可的类型变量使用推断类型?