我有一个“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.
答案 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