2013-11-01 61 views
3

我認爲我的問題是,我混淆了我對Haskell的多態性知識的瞭解,這些知識來自其他語言的超載和模板,在我的previous question之後,我以爲我對這些概念更好地掌握,但已經一次又一次,我想不是Haskell中的泛型函數...我不明白:(

無論如何,我想實現一個通用的距離函數,直截了當不夠:!

euclidean :: Integral a => [a] -> [a] -> Double 
euclidean a b = sqrt . sum $ zipWith (\u v -> (u - v)^2) x y 
       where x = map fromIntegral a 
         y = map fromIntegral b 

現在我想要將其應用於兩個矢量類型(其中,爲了說明起見,不能用於)定義):

type Vector1 = Integer 
data Vector2 = Vector2 Integer Integer 

閱讀的答案,我剛纔的問題後,我想我會用模式匹配去:

d :: a -> a -> Double 
d (Vector2 x1 y1) (Vector2 x2 y2) = euclidean [x1, y1] [x2, y2] 
d a b = euclidean [a] [b] 

這種失敗:

Couldn't match expected type `a' with actual type `Vector2' 
    `a' is a rigid type variable bound by 
     the type signature for d :: a -> a -> Double at test.hs:11:6 
In the pattern: Vector2 x1 y1 
In an equation for `d': 
    d (Vector2 x1 y1) (Vector2 x2 y2) = euclidean [x1, y1] [x2, y2] 

所以我想我請小心謹慎,再試試類型:

{-# LANGUAGE FlexibleInstances #-} 

class Metric a where 
    d :: a -> a -> Double 

instance Metric Vector1 where 
    d a b = euclidean [a] [b] 

instance Metric Vector2 where 
    d (Vector2 x1 y1) (Vector2 x2 y2) = euclidean [x1, y1] [x2, y2] 

這個編譯和工作你輸入的類型d是事先已知的。但是,就我而言,我已經編寫了另一個函數,它調用d,其中類型可以是(它是在運行時確定的)。這種失敗:

No instance for (Metric a) arising from a use of `d' 
Possible fix: 
    add (Metric a) to the context of 
    the type signature for someFunction :: [a] -> [a] -> [a] 
In the expression: d c x 
In the expression: (x, d c x) 
In the first argument of `map', namely `(\ x -> (x, d c x))' 

從我有限的回答我剛纔的問題的瞭解,我相信這樣做的原因是因爲我喜歡的類型類有孔,從而導致類型inferencer進入一個不確定的狀態。

在這一點上,我有點損失:既沒有參數也沒有ad hoc多態性解決了我的問題。因此,我的解決方案是這樣的:

someFunction1 :: [Vector1] -> [Vector1] -> [Vector1] 
-- Lots of code 
where d a b = euclidean [a] [b] 

someFunction2 :: [Vector2] -> [Vector2] -> [Vector2] 
-- Exactly the same code 
where d (Vector2 x1 y1) (Vector2 x2 y2) = euclidean [x1, y1] [x2, y2] 

這看起來不對。我錯過了什麼?

回答

8

在第一種情況,必須

d :: a -> a -> Double 

這意味着該函數可以與任何類型的被稱爲例如

d "blah" "blah" 

但您假設它是Vector2類型的實現。所以編譯器抱怨。

第二個錯誤本質上是一回事。你有

someFunction :: [a] -> [a] -> [a] 

再次假設a可以採取任何類型,但執行需要,這是Metric類型的,因爲您所呼叫的類型類的功能。這是編譯器在錯誤消息中建議的內容。所以你想用:

someFunction :: (Metric a) => [a] -> [a] -> [a] 
+1

我很高興這是一個簡單的遺漏。我開始瘋了,試圖瞭解我哪裏出錯!...謝謝你:) – Xophmeister

+1

一個好主意是(起初)省略類型簽名,看看編譯器推導出什麼。 – augustss

4

compiller告訴你,你應該限制一些功能。 您正在使用d但鍵入簽名狀態,表明它對任何a有效,但它應該僅對具有Metric實例的那個有效。

的解決方案是限制一個是類公制的:

someFunction :: (Metric a) => [a] -> [a] -> [a] 
+0

謝謝:)你回答第一,但@盧克泰勒的回答是「更充分」:所以+1爲你和剔他。 – Xophmeister