2012-02-29 130 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 
相關問題