我最近就share
的定义发布了关于question的syntactic-2.0。我已经在 GHC 7.6 :
{-# 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
使用该签名进行编译。或者,我可以用{/ 1>替换fi
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
这是SyntacticN
上的fundep隐含的类型。这允许我避免扩展。当然这是
我的问题是:
-XAllowAmbiguousTypes
的可接受用途吗?虽然我读过the docs,但我仍然无法确定约束是否含糊不清。具体来说,请考虑Data.Syntactic.Sugar中的这个函数:
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
。
答案 0 :(得分:12)
我没有看到任何已发布的syntactic版本,sugarSym
的签名使用了这些确切的类型名称,因此我将使用the development branch at commit 8cfd02^,这是仍使用这些名称的最后一个版本。
那么,为什么GHC会抱怨你的类型签名中的fi
而不是sugarSym
的{{1}}?您链接到的文档解释了如果类型不出现在约束的右侧,则类型是不明确的,除非约束使用函数依赖性从其他非模糊类型推断其他模糊类型。因此,让我们比较两个函数的上下文,并寻找函数依赖。
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编译了Syntactic。看着the code,确实如此。
尝试一下,似乎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不再抱怨重叠。
如果我是你,我会查看the definition of those two possible instances并确定这两个实现中的一个是否是你想要的那个。
答案 1 :(得分:2)
我发现AllowAmbiguousTypes
与TypeApplications
一起使用非常方便。考虑来自GHC.TypeLits的函数natVal :: forall n proxy . KnownNat n => proxy n -> Integer
。
要使用此功能,我可以写natVal (Proxy::Proxy5)
。另一种风格是使用TypeApplications
:natVal @5 Proxy
。 Proxy
的类型是由类型应用程序推断的,每次调用natVal
时都必须编写它是很烦人的。因此,我们可以启用AmbiguousTypes
并写入:
{-# Language AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}
ambiguousNatVal :: forall n . (KnownNat n) => Integer
ambiguousNatVal = natVal @n Proxy
five = ambiguousNatVal @5 -- no `Proxy ` needed!
但请注意,一旦你不明确,you can't go back!