Reputation: 9269
I would like to define a 3D vector, with x, y, z
coordinates. Vectors can be added using the (+)
operator and the length can be calculated using the length
function
I get the following error, if I would like to compile it:
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
The code is:
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
Upvotes: 3
Views: 427
Reputation: 120711
To literally overload the +
operator, you would need to define a Num
instance, like
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 = ...
This is in fact what the linear and hmatrix libraries do for their vector types, as does (basically) also the highly popular NumPy Python framework.
I would strongly recommend against this, because the semantics of vector spaces are in quite some sense incompatible with those of plain scalar numbers. In particular, you need to define multiplication here; the only way that properly works with these type signatures is component-wise, like with Matlab's .*
operator
Vec3 a b c * Vec3 t u w = Vec3 (a*t) (b*u) (c*w)
but that doesn't make mathematically sense for vectors as such, only for the expansion of vectors in a particular basis. Also it leads easily to bugs if you can erroneously define a vector as a single number (which in linear
pastes the number into all vector components, urgh!)
A better fit is the Monoid
class, as suggested by Reaktormonk. However you'll probably find yourself also wanting a scaling operation
(*^) :: Float -> Vec3 -> Vec3
μ *^ Vec3 t u w = Vec3 (μ*t) (μ*u) (μ*w)
(unlike component-wise multiplication, this is defined for any vector space), and Monoid
doesn't offer this.
The correct class for this sort of type is 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)
Upvotes: 4
Reputation: 21690
I tried going around by
import Prelude hiding ((+), length)
but then you don't have access to addition anymore. I'd recommend going the Monoid
route here. I'd rename length
to vlength
or similar, because IIRC there's no direct concept of that in any typeclass.
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
Upvotes: 3
Reputation: 12104
The answer is simple: you're trying to overload the + operator, which is already defined in the Prelude.
Haskell doesn't technically allow operator overloading the way most other languages do, so you can't just define a new function called +
because this function is already used for adding scalar numbers together.
Instead you could try calling it something else, like addV
, or maybe >+<
or something
Upvotes: 3