【问题标题】:Error binding type variables in instance of typeclass类型类实例中的错误绑定类型变量
【发布时间】:2012-02-29 15:13:04
【问题描述】:

我有一个“形状”类,它应该在所有实例上定义“区域”。 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.

【问题讨论】:

    标签: haskell instance typeclass


    【解决方案1】:

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

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

    它说的是“给我一个a,我会给你一个Area b,你想要的任何b;你可以选择”。因此,例如,我可以给area 一个Integer 并期望返回一个Area Double。显然,这不是你想要的!

    在这种情况下,出现错误是因为您使用了类型为 ax*y,而当预期 b 时,您必须提供一个适用于any 数字类型 b,但您给出的值仅适用于一个 (a)。

    如果您将area 的类型更改为a -> Area Integer 等,那么它会起作用。但是,我感觉您希望 instance 能够指定区域的类型。为此,您需要使用名为 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
    

    这表示对于每个类型a,它是Shape 的一个实例,都有一个关联类型AreaComponent a,表示其区域中每个组件的类型.根据Shape 的定义,该类型必须是Num 的实例。

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

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

    【讨论】:

    • 必须阅读类型族,因为我在考虑类似的方向。
    • 但是您的第二个解决方案太好了,可以满足我当前的需求。
    • @Karan:太好了!如果我的回答对您有帮助,您应该单击它旁边的复选标记将其标记为已接受:)
    【解决方案2】:

    问题是

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-30
      • 2023-03-07
      • 1970-01-01
      • 2013-10-22
      • 2013-10-10
      • 2020-01-13
      • 2013-04-21
      相关资源
      最近更新 更多