2012-02-29 112 views
1

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

Haskell有问题将b绑定到(x * y),其中x和y的类型是'a','a'也是类型类型Num。 我该如何解决这个问题? [如果我通过0代替(X * Y),它的工作原理,但不与(0 ::智力)甚至工作]

代码:

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. 

回答

5

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

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

什么它说的是“给我一个a,我给你一个Area b for 任何b你想要;你可以挑选。”所以,举例来说,我可以给areaInteger,并期望回一个Area Double。显然,这是不是你想要的!

在这种情况下,错误的出现是因为你使用x*y,其中有键入一个,当b预期 - 你必须给,对于任何数字类型b工作的价值,但你给一个值,该值仅适用于一个(一个

如果您更改了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,代表类型其区域的每个组成部分。该类型必须是Num的实例,其定义为Shape

你可以做的另一件事,如果你所有的形状需要数字类型的参数,是为了让这些实例是为每个形状类型的构造函数,而不是完整的形状类型本身:

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

instance Shape Rectangle where 
    area (Rectangle x y unit) = Area (x*y) unit 
+0

必须阅读关于类型族,因为我在类似的方向思考。 – Karan 2012-02-29 15:42:37

+0

但你的第二个解决方案太好了,适合我目前的需求。 – Karan 2012-02-29 15:44:38

+0

@Karan:太棒了!如果我的回答对您有帮助,您应该点击旁边的复选标记将其标记为已接受:) – ehird 2012-02-29 15:48:05

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 
相关问题