定义向量时Haskell不明确的出现(+)

时间:2017-03-18 10:42:48

标签: haskell

我想定义一个带有x, y, z坐标的3D矢量。可以使用(+)运算符添加向量,并且可以使用length函数计算长度

如果我想编译它,我会收到以下错误:

It could refer to either `Prelude.+',
                             imported from `Prelude' at hello.hs:1:1
                             (and originally defined in `GHC.Num')
                          or `Main.+', defined at hello.hs:9:14

代码是:

data Vec3 = Vec3 {
    x :: Float,
    y :: Float,
    z :: Float
} deriving (Eq,Show)

(+) :: Vec3 -> Vec3 -> Vec3
(Vec3 a b c) + (Vec3 t u w) = Vec3 (a+t) (b+u) (c+w)

length :: Vec3 -> Float
length (Vec3 a b c) = sqrt( a*a + b*b + c*c )

vv = Vec3 1.5 0.7 2.2

main :: IO ()
main = do
  print $ length vv

3 个答案:

答案 0 :(得分:4)

要真正重载+运算符,您需要定义一个Num实例,例如

instance Num Vec3 where
  Vec3 a b c + Vec3 t u w = Vec3 (a+t) (b+u) (c+w)
  Vec3 a b c * Vec3 t u w = ...

这实际上是linearhmatrix库为其矢量类型所做的事情,(基本上)也是非常受欢迎的NumPy Python框架。

我强烈建议反对这个,因为向量空间的语义在某种意义上与普通标量数字的语义不兼容。特别是,你需要在这里定义乘法;正确使用这些类型签名的唯一方法是组件方式,就像使用Matlab的.*运算符

一样
  Vec3 a b c * Vec3 t u w = Vec3 (a*t) (b*u) (c*w)

但这并不能在数学上对矢量本身有意义,只是为了在特定基础上扩展矢量 。如果你可以错误地将一个向量定义为单个数字(在linear中将数字粘贴到所有向量组件中,那么它很容易导致错误!呃!)

Monoid类更适合suggested by Reaktormonk。但是,你可能会发现自己也想要一个缩放操作

(*^) :: Float -> Vec3 -> Vec3
μ *^ Vec3 t u w = Vec3 (μ*t) (μ*u) (μ*w)

(与分量乘法不同,this is defined for any vector space),而Monoid不提供此功能。

此类型的正确类是VectorSpace

instance AdditiveGroup Vec3 where
  Vec3 a b c ^+^ Vec3 t u w = Vec3 (a+t) (b+u) (c+w)
  negateV v = (-1)*^v
instance VectorSpace Vec3 where
  type Scalar Vec3 = Float
  μ *^ Vec3 t u w = Vec3 (μ*t) (μ*u) (μ*w)

答案 1 :(得分:3)

答案很简单:你试图重载已经在Prelude中定义的+运算符。

Haskell技术上 技术上允许运算符重载大多数其他语言的方式,所以你不能只定义一个名为+的新函数,因为这个函数已经是用于将标量数字加在一起。

相反,您可以尝试将其称为其他内容,例如addV,或者>+<或其他

答案 2 :(得分:3)

我试过去

import Prelude hiding ((+), length)

但是你再也无权访问了。我建议你去Monoid路线。我将length重命名为vlength或类似名称,因为IIRC在任何类型类中都没有直接概念。

import Data.Monoid

data Vec3 = Vec3 {
    x :: Float,
    y :: Float,
    z :: Float
} deriving (Eq,Show)

instance Monoid Vec3 where
  mappend (Vec3 a b c) (Vec3 t u w) = Vec3 (a+t) (b+u) (c+w)
  mempty = Vec3 0 0 0

vlength :: Vec3 -> Float
vlength (Vec3 a b c) = sqrt( a*a + b*b + c*c )

vv = Vec3 1.5 0.7 2.2
v2 = Vec3 1.0 2.7 3.4

main :: IO ()
main = do
  print $ vv <> v2
  print $ vlength vv