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

Kar*_*ran 1 haskell instance typeclass

我有一个"Shape"类,它应该在所有实例上定义"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
Run Code Online (Sandbox Code Playgroud)

错误:

[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.
Run Code Online (Sandbox Code Playgroud)

ehi*_*ird 5

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

area :: (Num b) => a -> Area b
Run Code Online (Sandbox Code Playgroud)

什么它说的是"给我a,我会给你一个Area b任何 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
Run Code Online (Sandbox Code Playgroud)

这是说,对于每一个类型一个这是一个实例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
Run Code Online (Sandbox Code Playgroud)