Show, Eq not necessary for Num [Was: Revamping the numeric classes]

William Lee Irwin III wli@holomorphy.com
Sun, 11 Feb 2001 05:07:21 -0800


On 11-Feb-2001, Brian Boutel <brian@boutel.co.nz> wrote:
>> There may be some misunderstanding here. If you are talking about type
>> for which equality is always undefined, then I agree with you, but that
>> is not what I was talking about. I was thinking about types where
>> equality is defined for some pairs of argument values and undefined for
>> others - I think the original example was some kind of arbitrary
>> precision reals.

On Sun, Feb 11, 2001 at 06:24:33PM +1100, Fergus Henderson wrote:
> The original example was treating functions as a numeric type.  In the
> case of functions, computing equality is almost always infeasible.
> But you can easily define addition etc. pointwise:
> 	
> 	f + g = (\ x -> f x + g x)

I have a fairly complete implementation of this with dummy instances of
Eq and Show for those who want to see the consequences of this. I found,
interestingly enough, that any type constructor f with the following
three properties could have an instance of Num defined upon f a:

	(1) it has a unary constructor to lift scalars 
	(2) it has a Functor instance
	(3) it has an analogue of zip which can be defined upon it

or, more precisely:

\begin{code}
instance (Eq (f a), Show (f a), Num a, Functor f,
			Zippable f, HasUnaryCon f) => Num (f a)
	where
		f + g = fmap (uncurry (+)) $ fzip f g
		f * g = fmap (uncurry (*)) $ fzip f g
		f - g = fmap (uncurry (-)) $ fzip f g
		negate = fmap negate
		abs = fmap abs
		signum = fmap signum
		fromInteger = unaryCon . fromInteger

class Zippable f where
	fzip :: f a -> f b -> f (a,b)

class HasUnaryCon f where
	unaryCon :: a -> f a

instance Functor ((->) a) where
	fmap = (.)

instance Zippable ((->) a) where
	fzip f g = \x -> (f x, g x)

instance HasUnaryCon ((->) a) where
	unaryCon = const
\end{code}

and this generalizes nicely to other data types:

\begin{code}
instance Zippable Maybe where
	fzip (Just x) (Just y) = Just (x,y)
	fzip _ Nothing = Nothing
	fzip Nothing _ = Nothing

instance HasUnaryCon Maybe where
	unaryCon = Just

instance Zippable [ ] where
	fzip = zip

instance HasUnaryCon [ ] where
	unaryCon = cycle . (:[])
\end{code}

On 11-Feb-2001, Brian Boutel <brian@boutel.co.nz> wrote:
>> Returning to the basic issue, I understood the desire to remove Eq as a
>> superclass of Num was so that people were not required to implement
>> equality if they did not need it, not that there were significant
>> numbers of useful numeric types for which equality was not meaningful. 

On Sun, Feb 11, 2001 at 06:24:33PM +1100, Fergus Henderson wrote:
> The argument is the latter, with functions as the canonical example.

Well, usually equality as a mathematical concept is meaningful, but
either not effectively or efficiently computable. Given an enumerable
and bounded domain, equality may be defined (perhaps inefficiently)
on functions by

\begin{code}
instance (Enum a, Bounded a, Eq b) => Eq (a->b) where
	f == g = all (uncurry (==))
			$ zipWith (\x -> (f x, g x)) [minBound..maxBound]
\end{code}

and as I've said in another post, equality instances on data structures
expected to be infinite, very large, or where the semantics of equality
are make it difficult to compute, or perhaps even cases where it's just
not useful are also not good to be forced.


Cheers,
Bill