
I am a Haskell newbie working on my first serious test case, and I would like some feedback from the experts to make sure I am not doing anything stupid ;-) My applications are numerical (one goal of my current tests being to check how much of a performance penalty I will pay for using Haskell as opposed to C/C++) and involve a lot of geometry. Therefore I started by defining some appropriate types, in particular vectors. So here we go: -- Most general: element of a vector space. -- Operations: addition, subtraction, and multiplication by a scalar class Vect v where (<+>) :: Floating a => v a -> v a -> v a (<->) :: Floating a => v a -> v a -> v a (<*>) :: Floating a => a -> v a -> v a -- Vector space with a scalar product -- Adds dot product and norm class Vect v => VectSProd v where dot :: Floating a => v a -> v a -> a norm :: Floating a => v a -> a norm x = sqrt (dot x x) data Floating a => Vector a = Vector !a !a !a deriving (Eq, Show) -- Standard vector in 3D space instance Vect Vector where (<+>) (Vector x1 y1 z1) (Vector x2 y2 z2) = Vector (x1+x2) (y1+y2) (z1+z2) (<->) (Vector x1 y1 z1) (Vector x2 y2 z2) = Vector (x1-x2) (y1-y2) (z1-z2) (<*>) s (Vector x y z) = Vector (s*x) (s*y) (s*z) instance VectSProd Vector where dot (Vector x1 y1 z1) (Vector x2 y2 z2) = x1*x2+y1*y2+z1*z2 cross :: Floating a => Vector a -> Vector a -> Vector a cross (Vector x1 y1 z1) (Vector x2 y2 z2) = Vector (y1*z2-z1*y2) (z1*x2-x1*z2) (x1*y2-y1*x2) I would like to keep the numerical representation as general as possible, in particular to postpone decisions about precision (Float, Double, eventually arbitrary precision arithmetic) as much as possible. Therefore the "Floating a" constraint. Which leads to my first question: doing it the way I did, I find myself having to add a "Floating a" constraint to almost every function specification with a vector argument. I would prefer to specify once and for all that vector elements need to be "Floating", everywhere. Is there a way of doing that? Next, I would like to define some stuff for simulations of atomic systems. There are two kinds of environments for such simulations, infinite (no boundaries) and periodic (topologically a torus surface). The essential difference is the way that distances are calculated. -- General simulation universe properties class Universe u where distanceVector :: Floating a => u -> (Vector a) -> (Vector a) -> (Vector a) distance :: Floating a => u -> (Vector a) -> (Vector a) -> a distance u v1 v2 = norm (distanceVector u v1 v2) -- Infinite universe data InfiniteUniverse = InfiniteUniverse instance Universe InfiniteUniverse where distanceVector u v1 v2 = v2 <-> v1 -- Periodic universe data Floating a => OrthorhombicUniverse a = OrthorhombicUniverse a a a instance Floating a => Universe (OrthorhombicUniverse a) where distanceVector (OrthorhombicUniverse a b c) (Vector x1 y1 z1) (Vector x2 y2 z2) = Vector (fmod (x2-x1) a) (fmod (y2-y1) b) (fmod (z2-z1) c) where fmod x y = x - y*truncate (x/y) And here I run into a problem. The "Floating a" constraint on OrthorhombicUniverse has the same purpose as for vectors, the three arguments are the dimensions of the box that is periodically replicated. The compiler complains about the definition of distanceVector: ERROR "/home/hinsen/haskell/md/Universe.hs":24 - Inferred type is not general enough *** Expression : distanceVector *** Expected type : (Universe (OrthorhombicUniverse a), Floating b) => OrthorhombicUniverse a -> Vector b -> Vector b -> Vector b *** Inferred type : (Universe (OrthorhombicUniverse a), Floating a) => OrthorhombicUniverse a -> Vector a -> Vector a -> Vector a I do, in fact, want "Floating a" and "Floating b" to enforce the same type, but how can I do that? BTW, in case anyone wonders why I used a class for "Universe" instead of simply defining data Universe = InfiniteUniverse | OrthorhombicUniverse a a a the reason is that I want to leave the option of adding differently shaped universes later on. Konrad.
participants (1)
-
Konrad Hinsen