我认为我的问题是,我将我对Haskell多态性的非常有限的知识与其他语言的重载和模板等问题混为一谈。在我previous question之后,我认为我对这些概念有了更好的把握,但我再次尝试过,我猜不是!
无论如何,我正在尝试实现通用距离功能。这很简单:
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))'
根据我对前一个问题的答案的有限理解,我认为这是因为我的类型类中有漏洞,导致类型推理器进入不确定状态。
此时,我有点不知所措:参数和特殊多态都没有解决我的问题。因此,我的解决方案就是这个烂摊子:
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]
这似乎不对。我错过了什么?
答案 0 :(得分:8)
在第一种情况下,你有
d :: a -> a -> Double
表示可以使用任何类型调用该函数,例如
d "blah" "blah"
但您假设它在实现中的类型为Vector2
。所以编译器抱怨。
第二个错误基本上是一回事。
someFunction :: [a] -> [a] -> [a]
再次假定a
可以采用任何类型,但实现要求它是Metric
类型,因为您正在调用类型类函数。这是编译器在错误消息中建议的内容。所以你想使用:
someFunction :: (Metric a) => [a] -> [a] -> [a]
答案 1 :(得分:4)
compiller告诉你,你应该在someFunction中限制一个。 您正在使用d但键入签名状态,表明它对任何a都有效,但它应仅对具有度量标准实例的签名状态有效。
解决方案是将a限制为Metric类:
someFunction :: (Metric a) => [a] -> [a] -> [a]