[Haskell-cafe] Restricted type classes
wren ng thornton
wren at freegeek.org
Sat Sep 4 04:27:24 EDT 2010
On 9/4/10 3:50 AM, Ivan Lazar Miljenovic wrote:
> On 4 September 2010 17:40, wren ng thornton<wren at freegeek.org> wrote:
>> So, in the interest of generality, perhaps you should just pick a letter or
>> a short prefix and use that for each of the classes. In my blog posts I
>> called them 0-monads, 1-monads, and 2-monads; following Ganesh Sittampalam
>> and Matthieu Sozeau, they'd be monads, r-monads, and g-monads.
>
> I think I'd prefer to put the prefix on the kind * versions (though
> does a kind * version of something like Monad even make sense?).
Oh sure. I was just meaning that you should do something systematic like
have XFoo and YFoo, instead of having Foo and Bar. That way people can
just remember ({X,Y},{Foo,Fob,Fez}) instead of having to remember
{(Foo,Bar), (Fob,Baz), (Fez,Quux)}.
I'm a fan of keeping the undecorated names for the current versions, and
using a prefix for the * versions.
>> I'd say you should include: Functor, Foldable, Traversable, Pointed,
>> Applicative, Monad, and Monoid (both additive and multiplicative in separate
>> classes, as in the monoids package). Those eight make for a really
>> comprehensive toolkit that does most of the things people frequently need.
>> Of course, not every collection will have instances for all of them.
>
> Monoid was probably just going to be re-exported from base, since it's
> already for kind * (and as such works with all types, and doens't need
> any parametricity).
I was just talking general API, not necessarily what you need to write.
Reusing the standard classes is fine.
> Well, the point was that liftA/liftA2 might make more sense as being
> the methods to be defined for some types rather than<*>, but you
> could define them in terms of each other.
Well, liftA and liftM are just generic implementations of fmap using
pure/(<*>) and return/(>>=). If you can define fmap directly, then you
should do so. If you can't, you're always free to use the
implementations that liftA and liftM use. The liftA function is defined
explicitly for allowing you can say """instance Functor F where fmap =
liftA""". There's no reason for liftA to belong to Applicative, because
it has Functor as a superclass and therefore fmap exists. Since fmap can
be more efficient than liftA, it is better to use fmap when writing
code. Note that (<$>) is defined as fmap, and liftA2, liftA3, etc are
defined in terms of (<$>) i.e. fmap.
For liftM things are a bit hazier because Monad fails to mention Functor
as an explicit superclass, but it really ought to. Assuming it did, then
there's no reason for liftM to belong to Monad, because we're assured
that fmap exists. Though, again, liftM should be defined as a non-class
function in order to serve as a default implementation of fmap for those
who need it. This is the same reason why Data.Traversable defines
fmapDefault and foldMapDefault: not so that they can be used as
functions, but so that they can be used for giving class instances.
Perhaps we should be calling them fmapApplictiveDefault,
fmapMonadDefault, and fmapTraversableDefault instead...
I suppose you could use liftA2 as the basis of Applicative instead of
(<*>), but that seems a bit perverse to me. The (<*>) operation captures
exactly what we want to say--- namely the K axiom for modal logics;
which is equivalent to saying the applicative category has exponentials;
which is also the name for the whitespace of function application;... .
Whereas liftA2 seems like a far more round-about way of getting there.
There may be efficiency reasons for arguing that liftA2 should be
included in _addition_ to (<*>), but I'm not aware of them.
--
Live well,
~wren
More information about the Haskell-Cafe
mailing list