为什么这个Haskell类型类代码不起作用?

时间:2012-04-29 23:38:14

标签: haskell typeclass

试图找出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 个答案:

答案 0 :(得分:14)

签名

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

表示只要该类型属于intToIntClassIntClass就可以生成调用者想要的任何类型的值。但实现只能生成Int个值。

到目前为止,Int是唯一的实例是没有帮助的,就编译器而言,可能在其他模块中定义了更多的实例,因此编译器可以' t接受只生成Int s。

的方法

答案 1 :(得分:9)

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

当我们将它们组合在一起时,我们有一个函数将从调用者那里获取Int,然后直接返回。除非返回非常相同的Int,否则只要该类型是IntClass类型类的成员,它就是调用者想要的任何类型的值。如何将输入从Int变为调用者想要的任何(约束)类型?它不能。由于输入是直接返回的,因此返回类型必须与参数类型相同。类型检查器会从您的代码中推断出这一点,但是无法将该推断与您命名输出类型r进行协调。类型检查器希望与您合作,但不能确定rInt是同一的,因为rIntClass的一个实例的唯一假设1}}类型类。

fromInteger类型类中找到的函数与您编写的函数Num相似。如果你想谈论你可以基于Int构建的所有类型,那么这应该是你的类型类的方法。类似的东西:

class IntClass a where
  intToIntClass :: Int -> a

您可以为要成为IntClass实例的每种类型定义此方法,并且具有所需类型的intToIntClass :: IntClass r => Int -> r。此返回类型多态性主要取决于具有intToIntClass的适当定义的每个参与类型。

答案 2 :(得分:4)

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

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

问题是,intToIntClass的类型签名表示它从Int映射到 {em} 成员IntClass。编译器无法证明IntIntClass唯一成员,因此intToIntClass的主体刚刚返回Int,所以不够多态。

答案 3 :(得分:3)

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

instance IntClass Integer

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

  1. 将来可能会出现新实例,
  2. 今天的代码不能改变未来的行为
  3. 因此,编译器看到今天唯一的实例是Int,但它不能假设将来会保持这种情况。

    (1.)和(2.)的组合使得对Haskell代码的思考和推理变得更加容易。当你知道今天的新实例无法搞砸昨天的代码并且今天的代码从明天的新实例中获得安全时,有更少的警告。