Feel me ... this problem requires some explanation, but I think it is an interesting problem, and I believe that others have encountered it.
I would like to have a type which, as I know, will always have a value from 0 to 1 inclusive. This is easy enough to do, I can create the UnitInterval type and set only my intelligent constructor toUnitInterval and deconstructor fromUnitInterval .
{-
So far so good. But users of my module will find that arithmetic with UnitInterval and Double is messy. For instance,
λ> let a = toUnitInterval 0.5 λ> let b = 0.25 :: Double λ> toUnitInterval $ (fromUnitInterval a) * b UnitInterval 0.125
Of course, I could make UnitInterval derived instance of Num , so I can easily do arithmetic while I stick to UnitInterval s.
λ> a*a UnitInterval 0.25 λ> a+a+a UnitInterval 1.5 -- Oops! out of range
But I could write my own Num implementation for UnitInterval s, where type + operations check boundaries. But users of my module will need to perform complex calculations when partial results are not in range. Thus, they will have to convert everything to Double s, perform the calculations, and return to UnitInterval at the end.
But wait ... maybe there is a better way. I could make UnitInterval functor! An expression of the type fmap (\x -> x * exp x) a should give the result UnitInterval 0.8243606353500641 . Nice, clean code. Now Functor has the form (* → *) , and UnitInterval has the form * . But I can change it, like this ...
{-
But this does not compile. And looking back, I see this because I will need to limit the result of fmap , which would give it a signature of a different type than the one that was in Functor .
amy.hs:16:29: No instance for (Num b) arising from a use of 'toUnitInterval' Possible fix: add (Num b) to the context of the type signature for fmap ∷ (a → b) → UnitInterval a → UnitInterval b In the expression: toUnitInterval (fx) In an equation for 'fmap': fmap f (UnitInterval x) = toUnitInterval (fx) In the instance declaration for 'Functor UnitInterval'
Sigh ... back to the first version, with ugly arithmetic. Does anyone have a better solution?