使用类头中未包含的模糊类型变量来实现实例方法

时间:2019-01-31 22:13:49

标签: haskell types ghc typeclass ambiguous

说我有两个这样的课程:

{-# 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并推断出完美的类型。然后我想起b0bb0foo的类型中不可见,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工作。

1 个答案:

答案 0 :(得分:3)

似乎没有一种方法可以解决实例的歧义,因此,ProxyTagged似乎是定义类的必然方法,但是您可以将其包装为 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)