[Haskell-cafe] Numerics & implementing different instances of the same class

David Menendez dave at zednenem.com
Fri Dec 12 22:58:00 EST 2008

2008/12/12 George Pollard <porges at porg.es>:
> However, when it comes to defining (e.g.) a Field class you have two
> Abelian groups over the same type, which won't work straight off:

Especially since you generally can't take the multiplicative inverse
of the additive identity.

> I'm beginning to think that the best way to do things would be to drop
> the newtype wrappers and include instead an additional parameter of a
> type-level Nat to allow multiple definitions per type. Is this a good
> way to do things?

That depends on what you're trying to do. I don't think this is an
area where there is a single best solution.

I've occasionally toyed with labeled monoid classes, like this one:

    class Monoid label a where
        unit :: label -> a
        mult :: label -> a -> a -> a

    data Plus
    instance (Num a) => Monoid Plus a where
        unit _ = 0
        mult _ = (+)

... and so forth.

Even here, there are several design possibilities. For example, here
the label and the carrier jointly determine the operation, but you can
also have the label determine the operation and the carrier.

Moving on, you can then have:

    class (Monoid label a) => Group label a where
        inverse :: label -> a -> a

    class (Group labP a, Monoid labM a) => Ring labP labM a

Of course, you now need to provide labels for all your operations. I
suspect the overhead isn't worth it.

> Has anyone else done something similar? I've taken a look at the Numeric
> Prelude but it seems to be doing things a bit differently. (e.g. there
> aren't constraints on Ring that require Monoid, etc)

A couple of years ago, I suggested breaking Num into Monoid, Semiring,
Group, Ring, and something else for abs and signum.



    class Monoid a where
        zero :: a
        (+) :: a -> a -> a

    class (Monoid a) => Semiring a where
        one :: a
        (*) :: a -> a -> a

Semiring has laws which require one and (*) to form a monoid, so:

    newtype Product a = Product a

    instance (Semiring a) => Monoid (Product a) where
        zero = Product one
        Product x + Product y = Product (x * y)

Note that the Monoid instance is now a consequence of the Semiring
instance, rather than a requirement.

Dave Menendez <dave at zednenem.com>

More information about the Haskell-Cafe mailing list