Haskell中的通用函数......我不明白:(

Xop*_*ter 3 polymorphism haskell generic-programming

我认为我的问题是,我将我对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
Run Code Online (Sandbox Code Playgroud)

现在我想将它应用于两种向量类型(为了参数,不能重新定义):

type Vector1 = Integer
data Vector2 = Vector2 Integer Integer
Run Code Online (Sandbox Code Playgroud)

在阅读上一个问题的答案后,我想我会选择模式匹配:

d :: a -> a -> Double
d (Vector2 x1 y1) (Vector2 x2 y2) = euclidean [x1, y1] [x2, y2]
d a b = euclidean [a] [b]
Run Code Online (Sandbox Code Playgroud)

这失败了:

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]
Run Code Online (Sandbox Code Playgroud)

所以我想我会谨慎对待风并再次尝试输入类:

{-# 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]
Run Code Online (Sandbox Code Playgroud)

您提供的类型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))'
Run Code Online (Sandbox Code Playgroud)

从我对上一个问题的答案的有限理解,我认为这是因为我的类型类中有漏洞,这导致类型推理器进入不确定状态.

在这一点上,我有点失落:参数和特殊多态都没有解决我的问题.因此,我的解决方案是这个烂摊子:

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]
Run Code Online (Sandbox Code Playgroud)

这似乎不对.我错过了什么?

Sha*_*eep 8

在第一种情况下,你有

d :: a -> a -> Double
Run Code Online (Sandbox Code Playgroud)

例如,这意味着可以使用任何类型调用该函数

d "blah" "blah"
Run Code Online (Sandbox Code Playgroud)

但是你假设它是实现中的类型Vector2.所以编译器抱怨.

第二个错误基本上是相同的.你有

someFunction :: [a] -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

它再次假设a可以采用任何类型,但实现要求它是类型,Metric因为您正在调用类型类函数.这是编译器在错误消息中建议的内容.所以你想要使用:

someFunction :: (Metric a) => [a] -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)