我最近发布了一个关于syntactic-2.0的问题,关于share. 我已经在 GHC 7.6 中工作过:
share
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-} import Data.Syntactic import Data.Syntactic.Sugar.BindingT data Let a where Let :: Let (a :-> (a -> b) :-> Full b) 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 share = sugarSym Let
但是,GHC 7.8 想要-XAllowAmbiguousTypes使用该签名进行编译。或者,我可以fi用
-XAllowAmbiguousTypes
fi
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
这是fundep 上隐含的类型SyntacticN。这使我可以避免扩展。当然这是
SyntacticN
我的问题是:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f
sugarSym = sugarN . appSym
在我看来,fi(并且可能sup)在这里应该是模棱两可的,但它在没有扩展名的情况下编译。为什么是sugarSym明确的,share而是?既然share是一个应用程序sugarSym,所有的share约束都直接来自sugarSym。
sup
sugarSym
我没有看到任何已发布的语法版本,其签名sugarSym使用这些确切的类型名称,所以我将在提交 8cfd02^ 处使用开发分支,最后一个版本仍然使用这些名称。
那么,为什么 GHC 抱怨fiin 你的类型签名而不是那个 forsugarSym呢?您链接到的文档解释说,如果一个类型没有出现在约束的右侧,则它是不明确的,除非约束使用函数依赖关系从其他非歧义类型推断出其他不明确的类型。因此,让我们比较两个函数的上下文并寻找函数依赖关系。
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扩展。
sub
sig
f
f -> internal
f -> sig sym
ApplySym
AllowAmbiguousTypes
现在让我们来看看sugar。我注意到的第一件事是编译器不是在抱怨一个模棱两可的类型,而是在抱怨重叠的实例:
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]意思?
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 fior SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi),GHC 将不再抱怨重叠。
SyntacticN (a -> (a -> b) -> b) fi
SyntacticN (a -> f) (AST sym (Full ia) -> fi)
SyntacticN a fi
SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
如果我是你,我会查看这两个可能实例的定义,并确定这两个实现之一是否是你想要的。