说我有两个这样的课程:
{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes, FlexibleContexts #-}
class Foo a b
class Bar a where
foo :: Foo a b => a
值得注意的是,b
的{{1}}不能从使用foo
或实例头来推断。现在,我们尝试为此实现一个实例:
foo
到目前为止,一切都很好。在我们的实例中,data A = A
-- This implementation doesn't actually need the Foo A b constraint, but for the sake of the question it's required here.
a :: Foo A b => A
a = A
应该成为a
。它甚至具有正确的类型,对吗?因此,让我们继续实施实例:
foo
不幸的是,该实例无法编译,而是显示此错误:
instance Bar A where
foo = a
起初,此错误消息似乎很荒谬,因为看起来GHC可以统一instance-ambiguous.hs:15:9: error: …
• Could not deduce (Foo A b0) arising from a use of ‘a’
from the context: Foo A b
bound by the type signature for:
foo :: forall b. Foo A b => A
at /home/sven/instance-ambiguous.hs:15:3-5
The type variable ‘b0’ is ambiguous
• In the expression: a
In an equation for ‘foo’: foo = a
In the instance declaration for ‘Bar A’
|
Compilation failed.
和b
并推断出完美的类型。然后我想起b0
和b
在b0
或foo
的类型中不可见,GHC无法统一它们,因为它没有任何保证{ {1}}和a
实际上总是完全相同的,使用歧义类型时,这并不是意外错误。
通常,当我遇到此类错误时,我可以使用TypeApplications和ScopedTypeVariables这样解决它们:
b
在这里,我可以显式提供b0
,因为{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes, FlexibleContexts, TypeApplications, ScopedTypeVariables #-}
class Foo a b
class Bar b
foo :: forall b a. (Bar b, Foo a b) => a
foo = undefined
bar :: forall b a. (Bar b, Foo a b) => a
bar = foo @b
的类型签名将其引入了范围。因此,我尝试对实例执行相同操作(使用InstanceSigs):
@b
这也不编译,并给出此错误:
bar
我不确定,但是我认为这意味着GHC认为我的实例中的instance Bar A where
foo :: forall b. Foo A b => A
foo = a @b
引用了类声明中的其他instance-ambiguous.hs:16:10-31: error: …
• Could not deduce (Foo A b0)
from the context: Foo A b
bound by the type signature for:
foo :: forall b. Foo A b => A
at /home/sven/instance-ambiguous.hs:16:10-31
The type variable ‘b0’ is ambiguous
• When checking that instance signature for ‘foo’
is more general than its signature in the class
Instance sig: forall b. Foo A b => A
Class sig: forall b. Foo A b => A
In the instance declaration for ‘Bar A’
|
Compilation failed.
。
在Foo A b => A
上使用模式声明来获取范围内的原始b
也不起作用,因为实例声明中禁止模式绑定。
现在的问题:要解决此问题,我有什么选择?
我知道我只能在任何地方使用foo
(s / y / ie /),而不会听到任何歧义性问题,但是我通常会发现TypeApplications比b
更优雅,并且我想在这里使用它们,尤其是因为受影响的类是我的公共API的一部分。
我也可以将Proxy
作为Proxy
的类变量,但是我认为这会将b
的含义更改为我不想要的内容,因为那样的话,实例可以选择要为其实现实例的Bar
,但是我希望每个Bar
为存在b
的每个Bar a => a
工作。
答案 0 :(得分:3)
似乎没有一种方法可以解决实例的歧义,因此,Proxy
或Tagged
似乎是定义类的必然方法,但是您可以将其包装为 use < / em>带有TypeApplications
的类。
class Bar a where
foo_ :: Foo a b => proxy b -> a
foo :: forall b a. (Bar a, Foo a b) => a
foo = foo_ (Proxy @b)