Message: 20
Date: Sat, 04 Sep 2010 03:40:49 -0400
From: wren ng thornton <wren@freegeek.org>
Subject: Re: [Haskell-cafe] Restricted type classes
To: Haskell Cafe <haskell-cafe@haskell.org>
Message-ID: <4C81F801.606@freegeek.org>
Content-Type: text/plain; charset=UTF-8; format=flowed
On 9/3/10 12:16 AM, Ivan Lazar Miljenovic wrote:
> 2) How far should I go? Should I restrict myself to the
> "data-oriented" classes such as Functor, Traversable, etc. or should I
> try to make restricted versions of Applicative and Monad? Assuming I
> should:
I'd say you should do as much as seems reasonable. I tend to take things
as far as I can, but that'd end up doing a lot of the same work as
category-extras. For a general collections library, I think it'd make
sense to try to keep things simpler than that if possible. The simpler
it is, the better the uptake will be, so long as it's still complex
enough to capture what it needs to.
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.
Although I agree these do most things that people need, it's very rare that I need a data structure that guarantees *only* these features. Most often I need a map, a queue, etc. because I need either lookup by key, queue properties, etc. I think it's necessary to include classes like Indexable and Queue. Indexable may need to be split into two, one for ordered-Int indexes (i.e. lists) and one for Maps. Just a few days ago I wanted to change the priority queue implementation in some code. This was painful because the different available implementations all have varying APIs, so I couldn't just change the imports. I've wanted to do this in other contexts as well (e.g. maps and tries).
The perhaps more important use is for coding algorithms that depend upon map properties, queue properties, etc., but leaving the actual implementation polymorphic so it can be chosen by a higher level.
If the purpose of this project is to present a common interface for containers, then I think you should start by seeing what are the most common containers (I would guess lists, sets, maps, and queues) with a goal of providing their specific functionality through classes. Then all the common stuff can be stripped out and provided by superclasses. Of course we already know a great deal of the common operations, e.g. Traversable and Foldable, and we make use of those abstractions. It's this last step of a common map interface, queue interface, etc. that's missing.
If you're just going to provide stuff we already have, I don't really see the point.
> 2c) Should I keep the classes as-is, or should I explicitly put in the
> constraints mentioned in the Typeclassopedia (e.g. make Applicative an
> explicit superclass of Monad, and define return = pure for
> compatability reasons)? If so, should I bring over Pointed, etc. from
> category-extras to round out the set or just stick with classes that
> are already in base?
If you're defining a new hierarchy, I'd say you should do it correctly, i.e.
class Functor where fmap
class Functor => Pointed where unit -- or point
class Pointed => Applicative where (<*>) ; (<*) ; (*>)
class Applicative => Monad where (>>=) ; join
Shouldn't it be:
class Functor where fmap
class Pointed where point
class (Functor f, Pointed f) => PointedFunctor f where
class PointedFunctor f => Applicative f where (<*>); --etc.
class Applicative f => Monad f where (>>=); --etc.
even I might omit PointedFunctor, though, because it doesn't add anything. If it is omitted, that just means your Applicative contexts are slightly longer. But please don't make Pointed depend on Functor - we've already seen that it won't work for Bloom filters.
Cheers,
John