如何摆脱这种模棱两可?

时间:2011-12-07 13:37:21

标签: haskell functional-programming ghc ambiguity type-systems

我很确定之前已经问过这个问题,但我无法找到正确的答案:

我试图消除以下示例代码段中的含糊之处:

{-# LANGUAGE MultiParamTypeClasses #-}

class FooBar a b where
    foo :: a -> a
    foo = id
    bar :: a -> a
    bar = foo        -- ERROR AT THIS LINE

我收到如下错误消息:

Ambiguous type variable `b0' in the constraint:
      (FooBar a b0) arising from a use of `foo'
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: foo
    In an equation for `bar': bar = foo

这是可以理解的。但请注意,我实际上无法遵循编译器的建议并修复有问题的类型变量:foo在其类型签名中不包含b类型变量。这也行不通:

    bar = (foo :: FooBar a b => a -> a) -- ERROR, the same error message

即使启用了-XScopedTypeVariables也没有。

如何告诉GHC哪个FooBar可以使用?它甚至可能吗?

修改

以下答案:是的,我听说过功能依赖和相关类型。

关于功能依赖 - 我正在寻找一种方法来明确告知GHC使用哪个实例(而不是要求它自己导出正确的类型)。

至于两者 - 在现实世界的例子中,我实际上需要两个参数ab是独立的。

再次:我知道这个例子非常愚蠢,我知道b没有在类型的主体中使用,这没有任何意义。这是对现实世界示例的极端简化,实际上确实有意义。真实用例涉及使用a类型b类型的“可替代性”,然后使用类型a使用类型bc的“一致性”,不幸的是更长。

修改(2)

好的抱歉。我认为你毕竟我已经说服了我,我必须重构代码,以便不会有任何不明确的函数(即那些不包含其类型签名中所有独立类型的函数)。和你说话确实让我想到了这件事。赞赏。

2 个答案:

答案 0 :(得分:8)

即使您从班级中完全删除bar,您的班级也会定义不明确。我们假设您有以下课程:

class FooBar a b
  where
    foo :: a -> a
    foo = id

现在假设你想在这个类的定义之外使用foo

e = .... foo x .....

GHC会抱怨与您的错误消息类似的错误消息,它找不到b的类型。仅仅因为您的代码不提供这样的类型。您的类型类声明表示每对类型ab都可能获得一个实例。因此,即使在a的调用中修复了foo,仍然可以选择任意多个可能的实例。

函数依赖a -> b将解决该问题,因为它指出:给定类型a,可能只有Foobar a b的一个实例。相关类型通过不使用多参数类型类来避免此问题。

关于OP的编辑:

如果您坚持使用两种类型参数。然后,类型类的每个方法的签名都必须包含类的每个类型变量。没有办法解决它。

答案 1 :(得分:6)

如果你引入了一个函数依赖项class FooBar a b | a -> b where,它会解决它,但如果没有它(或相关类型),我认为不可能解析要使用的实例。

哦,一种方式,但有点丑:引入类型为b的虚拟参数

class FooBar a b where
    foo' :: b -> a -> a
    foo' _ = id
    bar' :: b -> a -> a
    bar' b = foo b
相关问题