【问题标题】:How to get rid of this ambiguity?如何摆脱这种歧义?
【发布时间】:2012-01-15 00:22:36
【问题描述】:

我很确定以前有人问过这个问题,但是我找不到正确的答案:

我试图消除以下示例代码 sn-p 中的歧义:

{-# 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 没有用在类型类的主体中,这没有任何意义。这是对现实世界示例的极端简化,这实际上是有道理的。真正的用例涉及使用 b 类型的 a 类型的“可替代性”,然后使用 c 类型的 ab 类型的“统一性”,不幸的是要长得多。

编辑(2)

好的,抱歉。我想你毕竟说服了我,我必须重构代码,以免有任何定义不明确的函数(即在其类型签名中不包含所有独立类型的函数)。不过,和你交谈真的帮助我思考了这件事。赞赏。

【问题讨论】:

  • 您能解释一下为什么要这样做吗? FooBar的实现只依赖a,不依赖b,那提b有什么意义呢?
  • @lbolla 是的,我可以解释一下,但是这将花费这里所有代码 sn-ps 的两倍,并且涉及的不是一个而是 3 个不同的类型类。恐怕你必须相信我。这只是一个示例,可以使用需要ab 的其他方法进行扩展。
  • @julkiewicz:我已经编辑了我的答案来回答你的编辑。

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


【解决方案1】:

如果你引入一个函数依赖,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

【讨论】:

  • 是的,我已经阅读了有关函数依赖和关联类型的信息——两者都可以提供帮助。问题是,在这种情况下,GHC 真正缺乏的不是一些复杂的类型系统功能,它可以自行派生出正确的类型。它只是缺少明确告知它应该使用哪些类型的语法。至少我是这么看的。
  • 我见过的一个 hack 比添加 b 类型的虚拟参数稍微丑一点,就是先添加 data Proxy a = Proxy,然后添加 Proxy b 类型的虚拟参数。它避免了“好吧,undefined 没问题,因为函数从不查看它”的混乱推理。
  • @DanielWagner:但你还是得把它叫做foo x (Proxy :: SomeType)。这比foo x (undefined :: SomeType) 好太多了吗?我更喜欢未定义。
  • @jmg 我猜没有考虑口味。 =)
  • 以前从未见过Proxy。我比undefined 更喜欢它,因为我不需要查看API 来查看代码是否会崩溃。
【解决方案2】:

即使您将bar 从课程中完全删除,您的课程也是不明确的。假设您有以下课程:

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

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

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

GHC 会抱怨与您的错误消息类似的错误消息,即找不到b 的类型。仅仅是因为您的代码不提供这种类型。您的类型类声明说每对类型 ab 可能会获得一个实例。因此,即使afoo 的调用中被修复,仍然有任意多个可能的实例可供选择。

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

关于 OP 的编辑:

如果你坚持有两个类型参数。那么类型类的每个方法的签名都必须包含该类的每个类型变量。没有办法解决。

【讨论】:

  • 好吧,我明白了,毕竟我把我的例子过于简单化了。我会在编辑中写一个新版本。
  • @julkiewicz:您可以随时添加缺失类型的虚拟参数。
猜你喜欢
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
  • 1970-01-01
  • 2022-07-30
相关资源
最近更新 更多