Things that amuse me

Monday, May 07, 2007

I was asked if my fixed number module (Data.Number.Fixed) which has the precision built into the type could be used with a dynamic epsilon, i.e., one that is not know at compile time. The answer is, yes. It only takes a fraction of an oleg to do.
A recap:
The idea with the Fixed type was that, e.g., Fixed Eps1, is the type where computation happens with an epsilon of 1. To get the epsilon for a type I have a class

So what do we do if we need an epsilon of say, 0.01? Well, there's another instance for the type constructor EpsDiv10:

instance (Epsilon e) => Epsilon (EpsDiv10 e) where
eps x = eps x / 10

So the type EpsDiv10 (EpsDiv10 Eps1) will have an epsilon of 0.01. Similarly, with the right set of primitive types and type constructors you could build any epsilon you want.
To simplify a little the function below only finds an epsilon within a factor of 10 from the requested one.

How does it work? It's starts by passing something of type Eps1 to loop, and then the loop uses that epsilon if it is small enough, otherwise it will pass something that have an epsilon that is a tenth to the next iteration of the loop.
There are two "tricks" here, first the loop function recurses with an argument of a different type than it was given. So it needs polymorphic recursion, and thus needs a type signature. Second, the function, f, has a rank two type.
This way the function can work with any epsilon it is given; otherwise we would
not know that.
Note how the argument to loop is actually never ysed, so undefined works great. All we need is the right type. And with scoped type variables this is easy to do.
There are some improvements that should be made to this function: it is inefficient and it doesn't give you the exact epsilon back. But it has the right general idea.