
On 11/28/06, Brian Hulley
Slavomir Kaslev wrote:
I have to define a couple of float2, float3, float4 Cg, HLSL style vectors in Haskell. At first I was tempted to make them instances of Num, Floating, RealFrac, etc. but some of the functions defined in those classes have no sense for vectors.
I'd suggest that this implies that these classes are not suitable for Vectors.
One such example is signum from class Num.
There are several workarounds for this. One may come up with some meaning for vectors of such functions, for example:
instance Num Float3 where ..... signum a | a == Float3 0 0 0 = 0 | otherwise = 1
This looks a bit unnatural. Also, testing equality of Floats is not generally recommended.
Agreed about the testing floats remark. The former definition was just to scratch the point that it's silly to come up with new meanings of such operations. It's not well typed either. signum is Num a => a->a, while the former is (Vector b, Num a) => b -> a. After giving some thought on signum, I got to the point, that signum should be defined so that abs x * signum x = x holds. So it can be defined as signum (Vec2 x y) = Vec 2 (signum x) (signum y). It turns out that all the functions in Num, Floating, etc. classes can be given meaningful definitions for vectors in this pattern. That is f (Vecn x1 x2 .. xn) = Vecn (f x1) ... (f xn). And all expected laws just work. One can think of that like the way SIMD processor works, it does the same operations as on floats but on four floats at parallel. So vectors can be instances of Num, Floating, etc., after all.
[snip] I know that I can scrap all those Num, Floating, RealFrac, etc. classes and define class Vector from scratch, but I really don't want to come up and use different names for +, -, etc. that will bloat the code.
While it may be tempting to want to use symbolic operators like + and -, these quickly become very confusing when more distinctions need to be made (eg between cross product, dot product, and scaling, or between transforming a position versus transforming a direction) so I'd argue that for readability descriptive names are better than symbols:
class Num a => Vector v a where plus :: v a -> v a -> v a minus :: v a -> v a -> v a cross :: v a -> v a -> v a dot :: v a -> v a -> a scale :: a -> v a -> v a magSquared :: v a -> a
As I already said, I am leaning toward making vectors instances of Num.
class Num a => Transform mat vec a where transformPosition :: mat a -> vec a -> vec a transformDirection :: mat a -> vec a -> vec a
instance Num a => Transform Mat44 Vec4 a where -- ...
If you're doing matrix transformations, you might also like to consider using separate PositionN and DirectionN types instead of VecN to make use of the type system to catch some math bugs but I haven't looked into this myself yet so I don't know whether this would be practical or not.
The point of library is to define vectors, not as mathematical entities, but just like data representation, as they are defined in Cg/HLSL. One can take other approach and differentiate vectors between positions and directions as you suggested. In Renderman shading language, for example, those two different types are called 'point' and 'normal'. Cheers -- Slavomir Kaslev