
On 4 September 2010 17:40, wren ng thornton
On 9/3/10 12:16 AM, Ivan Lazar Miljenovic wrote:
1) How should I name the kind * versions? For example, the kind * version of Functor is currently called Mappable with a class method of rigidMap. What should I call the kind * version of Foldable and its corresponding methods? Is there a valid system I can use for these?
I think it'd be good to be consistent, whatever you do. For (*->*->*) monads I've seen them called GMonads (for "generalized") and indexed monads. For the (*->*) monads, I've seen RMonads and parameterized monads. Here are some links for those who may be interested; they have more links to independent invention by Oleg Kiselyov and also by Bob Atkey, as well as a Coq implementation and a mention of Agda. (For my part, I've since moved on to playing with monads between different categories, which isn't really germane here.)
http://winterkoninkje.dreamwidth.org/65416.html http://winterkoninkje.dreamwidth.org/65585.html
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?).
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.
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).
2b) Is it OK to promote functions that use a class to being class methods? When I was discussing this in #haskell several people mentioned that defining something like liftA2 for the Set instance of (restricted) Applicative would make more sense than the default<*> (since (a -> b) isnt' an instance of Ord).
That depends. In general, it's better to have smaller classes because it reduces the burden on programmers writing instances and it allows for smaller dictionaries at runtime. In general, I'd say that functions should be included into the class when they often permit specialized definitions which are more efficient than the generic one. And of course, they should be included when they serve as the basis of the class (e.g., both (==) and (/=) should be included in Eq since they both serve as a basis for equality, with no one being obviously easier to implement than the other). If there's little chance of a performance gain, then why would you want it in the class at all?
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.
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
There's no benefit to preserving the ill designs of the past.
Yes, that was my point.
3) Am I wasting my time with this?
Not at all. Many people want a good containers API, and many people want a cleaned up version of the categorical classes which isn't quite as involved as category-extras. Go for it!
*sigh* I was almost wishing people would say I _was_ wasting my time with this... ;-) -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com IvanMiljenovic.wordpress.com