1

试图找出 Haskell 类型类。为什么以下不起作用?

{-# LANGUAGE FlexibleInstances #-}
class IntClass t
instance IntClass Int

intToIntClass  :: (IntClass r) => Int -> r
intToIntClass  x = x

显然,“实例”并不意味着我认为它应该意味着什么。相反,它给出了我不明白的错误消息。

Could not deduce (r ~ Int)
from the context (IntClass r)
  bound by the type signature for intToIntClass  :: IntClass r => Int -> r
  at f.hs:10:1-16
  `r' is a rigid type variable bound by
      the type signature for intToIntClass  :: IntClass r => Int -> r
      at f.hs:10:1
In the expression: x
In an equation for `intToIntClass t': intToIntClass  x = x
4

4 回答 4

14

签名

intToIntClass  :: (IntClass r) => Int -> r

意味着intToIntClass可以产生调用者想要的任何类型的值,只要该类型属于IntClass. 但实现只能产生Int价值。

没有帮助的Int是 - 到目前为止 - 唯一的实例,就编译器而言,可能在其他模块中定义了更多实例,因此编译器不能接受仅产生Ints 的方法。

于 2012-04-29T23:46:43.170 回答
9

你写的函数说,“给我一些东西,我会把它还给你。” 您编写的类型说:“我可以将 anInt转换为调用者想要的任何类型,只要该类型是类型类的实例IntClass。”

当我们把它们放在一起时,我们有一个函数可以Int从调用者那里获取一个,然后直接返回。除了当返回相同Int的值时,它将是调用者想要的任何类型的值,只要该类型是类型类的成员IntClass。输入如何从Int调用者想要的任何(受约束的)类型变为?它不能。由于输入是直接返回的,因此返回类型必须与参数类型相同。类型检查器从您的代码中推断出这一点,但随后无法将该推断与您命名输出类型相协调r。类型检查器希望与您一起解决此问题,但不能确定这rInt给定唯一假设r是类型类实例的情况相同IntClass

在typeclassfromInteger中找到了一个与您编写的函数很相似的函数。Num如果您想谈论可以基于 an 构造的所有类型Int,那么这应该是您的类型类的方法。就像是:

class IntClass a where
  intToIntClass :: Int -> a

您将为您希望成为 实例的每种类型定义此方法IntClass,并具有您想要的intToIntClass :: IntClass r => Int -> r. 这种返回类型多态性严重依赖于每个参与类型具有适当的定义intToIntClass

于 2012-04-29T23:50:10.283 回答
4

你的类型很好。例如,这有效:

intId :: IntClass r => r -> r
intId = id

问题是,的类型intToIntClass签名说它映射Int到. 编译器无法证明 that是 的唯一成员,因此由于严格返回的主体,它的多态性不够。IntClassIntIntClassintToIntClassInt

于 2012-04-29T23:48:26.163 回答
3

为了进一步澄清,假设我将您的模块与一个新实例一起使用:

instance IntClass Integer

然后你的实现intToIntClass也承诺有 type (IntClass Integer => Int -> Integer)。但是您的实现与此冲突。编译器假定类型类是“开放的”,这意味着

  1. 未来可能会出现新的实例,并且
  2. 今天的代码不能在这样的未来改变行为

因此,编译器看到今天唯一的实例是 for,Int但它不能假设这将在未来保持不变。

(1.) 和 (2.) 的结合使思考和推理 Haskell 代码变得更加容易。当您知道今天的新实例不能搞砸昨天的代码并且今天的代码对明天的新实例是安全的时,警告要少得多。

于 2012-04-30T07:06:58.647 回答