4

这是一个初学者的问题,但我无法在任何地方找到任何答案。
以下代码:

class A a where
  foo :: a
class A a => B a where
  bar :: a
  bar = (foo :: a)

无法在 GHC 中编译,并显示错误消息:

Could not deduce (A a1) arising from a use of `foo'
from the context (B a)
  bound by the class declaration for `B'
...

GHC 似乎不相信 typeclass B 定义中的所有 a 都是相同的。谁能解释一下它的推理路线到底是什么?

删除第 5 行中的类型注释当然可以避免这个问题,但我仍然想了解这里发生了什么......

4

2 回答 2

11

您确实应该摆脱类型注释。类型变量在 Haskell 中没有作用域,所以(foo :: a). 被解释为“已经为任何类型foo生成类型值”,这是无法完成的,因为只会生成类中那些类型的值。aafooaA

换句话说,您的声明B相当于

class A a => B a where
  bar :: a
  bar = (foo :: c)

也就是说,您对类型变量a的使用与声明中的其他使用之间没有联系。

删除显式注释可以解决您的问题:

class A a => B a where
  bar :: a
  bar = foo

现在,编译器可以确定您要调用foo的类型a,即您在签名中写入的类型,bar并且出现在类声明的头部。

Glasgow Haskell 编译器 (GHC) 带有一个允许范围类型变量的扩展。启用该扩展后,您的片段将像您最初预期的那样进行类型检查:

{-# LANGUAGE ScopedTypeVariables #-}
class A a where
  foo :: a
class A a => B a where
  bar :: a
  bar = (foo :: a)
于 2012-04-20T10:02:38.533 回答
2
Prelude> :set -XScopedTypeVariables
Prelude> :{
Prelude| class A a where
Prelude|   foo :: a
Prelude| class A a => B a where
Prelude|   bar :: a
Prelude|   bar = (foo :: a)
Prelude| :}
Prelude> :t bar
bar :: B a => a
Prelude> 

但正如 dblhelox 所说,这里没有必要使用范围类型变量。

于 2012-04-20T10:07:23.467 回答