I thought I would try modeling some numerical integration on vector quantities of different dimensionality, and figured that type classes were the way to go. I needed something to define the difference between two values and to scale it by a multiplier (to get the derivative), as well as being able to take the distance function.
So far I have:
class Integratable a where
difference :: a -> a -> a
scale :: Num b => a -> b -> a
distance :: Num b => a -> a -> b
data Num a => Vector a = Vector1D a | Vector2D a a
instance Num a => Integratable (Vector a) where
difference (Vector1D x1) (Vector1D x2) = Vector1D (x1 - x2)
scale (Vector1D x) m = Vector1D (x * m)
distance (Vector1D x1) (Vector1D x2) = x1 - x2
difference (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 - x2) (y1 - y2)
scale (Vector2D x y) m = Vector2D (x * m) (y * m)
distance (Vector2D x1 y1) (Vector2D x2 y2) = sqrt((x1-x2)*(x1-x2)
+ (y1-y2)*(y1-y2))
Unfortunately there are a couple of problems here that I haven’t figured out how to resolve. Firstly, the scale function gives errors. GHC can’t tell that m and x are compatible since the rigid type restriction Num is given in the instance in one case, and in the Vector type in the other case… Is there a way to specify that x and m are the same type?
(I realize in fact that even if x and m are both Num, they may not be the same Num. How can I specify this? If I can’t figure it out with Num, using Double would be fine, but I’d rather keep it general.)
There’s a similar problem with distance. Attempting to specify that the return type is Num fails, since it can’t tell in the instance definition that a is going to contain values that are compatible with b.
EDIT: It seems to me now that the article on functional dependencies from the HaskellWiki provides the key information in the best form that I can find, so I’d suggest reading that instead of my answer here. I’m not removing the rest of the content, though, as it makes clear (I hope) why FDs are useful here.
Apart from the grouping of definitions issue which Dave pointed out…
This is the main problem, actually. You can’t multiply an
Integerby aFloat, say. In effect, you need thexand themin scale to be of the same type.Also, a similar issue arises with distance, with the additional complication that
sqrtneeds aFloatingargument. So I guess you’d need to mention that somewhere too. (Most likely on the instance, I guess).EDIT: OK, since
sqrtonly works onFloatingvalues, you could roll a typeclass for those to upcastFloats toDoubles when needed.Another idea involves having a typeclass
Scalable:This uses a so-called functional dependency in the definition of
Scalable. In fact, trying to remember the syntax for that, I found this link… So I guess you should disregard my inferior attempt at being helpful and read the quality info there. 😉I think you should be able to use this to solve your original problem.