在类型类的实例中绑定类型变量时出错

时间:2012-02-29 15:13:05

标签: haskell instance typeclass

我有一个“Shape”类,它应该在所有实例上定义“area”。 area返回“Area b”(数据类型),其中包含一个数字(b属于Num类型类),表示该Shape的区域。

Haskell有问题将b绑定到(x * y),其中x和y的类型为'a',而'a'也是类型类型Num。 我该如何解决这个问题? [如果我将(x * y)替换为0,它可以工作,但即使用(0 :: Int)]

也不起作用

代码:

data Unit = Unit | Meter | CentiMeter               deriving Show

data Area a = Area a Unit                           deriving Show

class Shape a where
      area :: (Num b) => a -> Area b

data Rectangle side = Rectangle side side Unit  deriving Show

instance (Num a) => Shape (Rectangle a) where
     area (Rectangle x y unit) = Area (x*y) unit

错误:

[1 of 1] Compiling Main             ( y.hs, interpreted )

y.hs:11:46:
    Could not deduce (a ~ b)
    from the context (Num a)
      bound by the instance declaration at y.hs:10:10-39
    or from (Num b)
      bound by the type signature for
                 area :: Num b => Rectangle a -> Area b
      at y.hs:11:10-52
      `a' is a rigid type variable bound by
          the instance declaration at y.hs:10:15
      `b' is a rigid type variable bound by
          the type signature for area :: Num b => Rectangle a -> Area b
          at y.hs:11:10
    In the second argument of `(*)', namely `y'
    In the first argument of `Area', namely `(x * y)'
    In the expression: Area (x * y) unit
Failed, modules loaded: none.

2 个答案:

答案 0 :(得分:5)

这里的问题是area的类型签名:

area :: (Num b) => a -> Area b

它说的是"给我一个a,我会给你一个Area b 任何 b你想要的;你可以选择"。因此,举例来说,我可以area Integer并期待Area Double。显然,这不是你想要的!

在这种情况下,出现错误的原因是您使用x*y,其类型为 a ,当预计 b 时 - 您必须提供一个值适用于任何数字类型 b ,但您提供的值仅适用于一个( a )。

如果您将area的类型更改为a -> Area Integer等,那么它就可以了。但是,我有一种感觉,您希望实例能够指定区域的类型。为此,您需要使用名为type families的语言扩展程序:

{-# LANGUAGE TypeFamilies #-}

class (Num (AreaComponent a)) => Shape a where
    type AreaComponent a
    area :: a -> Area (AreaComponent a)

instance (Num a) => Shape (Rectangle a) where
    type AreaComponent (Rectangle a) = a
    area (Rectangle x y unit) = Area (x*y) unit

这表示对于Shape的每个类型的 ,都有关联类型 AreaComponent a,代表其所在地区的每个组成部分的类型。根据{{​​1}}的定义,该类型必须是Num的实例。

如果您的所有形状都采用数字类型参数,那么您可以做的另一件事是使实例适用于每个形状的类型构造函数,而不是完整的形状类型本身:

Shape

答案 1 :(得分:3)

问题在于

class Shape a where
      area :: (Num b) => a -> Area b

承诺能够提供调用者可能需要的任何Num类型,但您的实现只提供被调用者提供的一些Num类型。

生成任何所需类型数字的唯一方法是使用fromInteger(或文字,隐含fromInteger。)

为了能够提供由要计算其区域的事物类型确定的某些Num类型,您可以使用多参数类型类和函数依赖项或类型族,例如

{-# LANGUAGE TypeFamilies #-}

class Shape a where
    type NumType a :: *
    area :: a -> Area (NumType a)

instance (Num side) => Shape (Rectangle side) where
    type NumType (Rectangle side) = side
    area (Rectangle w h u) = Area (w*h) u