我很确定之前已经问过这个问题,但我无法找到正确的答案:
我试图消除以下示例代码段中的含糊之处:
{-# 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使用哪个实例(而不是要求它自己导出正确的类型)。
至于两者 - 在现实世界的例子中,我实际上需要两个参数a
和b
是独立的。
再次:我知道这个例子非常愚蠢,我知道b
没有在类型的主体中使用,这没有任何意义。这是对现实世界示例的极端简化,实际上确实有意义。真实用例涉及使用a
类型b
类型的“可替代性”,然后使用类型a
使用类型b
和c
的“一致性”,不幸的是更长。
修改(2)
好的抱歉。我认为你毕竟我已经说服了我,我必须重构代码,以便不会有任何不明确的函数(即那些不包含其类型签名中所有独立类型的函数)。和你说话确实让我想到了这件事。赞赏。
答案 0 :(得分:8)
即使您从班级中完全删除bar
,您的班级也会定义不明确。我们假设您有以下课程:
class FooBar a b
where
foo :: a -> a
foo = id
现在假设你想在这个类的定义之外使用foo
。
e = .... foo x .....
GHC会抱怨与您的错误消息类似的错误消息,它找不到b
的类型。仅仅因为您的代码不提供这样的类型。您的类型类声明表示每对类型a
和b
都可能获得一个实例。因此,即使在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