[Haskell-cafe] Avoiding name collisions by using value spaces instead of modules

Cale Gibbard cgibbard at gmail.com
Sun Jan 8 15:33:39 EST 2006


On 08/01/06, Brian Hulley <brianh at metamilk.com> wrote:
>
> ----- Original Message -----
> From: "Cale Gibbard" <cgibbard at gmail.com>
> To: "Brian Hulley" <brianh at metamilk.com>
> Cc: "Daniel Fischer" <daniel.is.fischer at web.de>; "Haskell-cafe"
> <haskell-cafe at haskell.org>
> Sent: Sunday, January 08, 2006 5:54 PM
> Subject: Re: [Haskell-cafe] Avoiding name collisions by using value spaces
> instead of modules
>
>
> >On 08/01/06, Brian Hulley <brianh at metamilk.com> wrote:
> >> For example, suppose I'm writing a module M that deals with grammar,
> >> where
> >> the elements in a grammar rule are parameterised so that rules can be
> >> written using strings but processed as if we'd used ints instead:
> >>
> >>     data Element a = Terminal a | Nonterminal a | Action a
> >>
> >>     data Rule a = Rule (Element a) [[Element a]]
> >>
> >> Now I want to convert elements and rules from a to Int, so at the moment
> >> I
> >> have to write:
> >>
> >>     convertElement :: Element a -> CM (Element Int)
> >>     ...
> >>
> >>     convertRule :: Rule a -> CM (Rule Int)
> >>
> >> for some appropriate monad CM.
> >> Whereas I would have much preferred to use just the word "convert" in
> >> both
> >> cases. It is tremendously annoying to have to suffix everything with the
> >> type name.
>
> >This is what typeclasses are for.
>
> >class Convert c where
> >    convert :: c a -> CM (c Int)
>
> Type classes just seem overkill for this kind of thing. All I want is
> compile time resolution of an overloaded identifier, whereas type classes
> give all the machinery that would be needed if I wanted runtime ad-hoc
> polymorphism, with all the attendant verbosity, just so that the compiler
> can then optimize out the runtime polymorphism behind the scenes for cases
> like the example above.

There's no such runtime polymorphism going on there. Barring
existential types, everything is always resolved at compile time.
Further, I wouldn't consider the polymorphism ad-hoc, so much as it is
a restricted form of parametric polymorphism. Further, it's not really
that much typing. You type:

class Convert c where
    convert :: c a -> CM (c Int)

instance Convert Rule where
    convert (Rule lhs rhss) = ...

instance Convert Element where
    convert (Terminal t) = ...
    convert (Nonterminal n) = ...
    convert (Action a) = ...

rather than

convertRule :: Rule a -> CM (Rule Int)
convert (Rule lhs rhss) = ...

convertElement :: Element a -> CM (Element Int)
convert (Terminal t) = ...
convert (Nonterminal n) = ...
convert (Action a) = ...

which is 8 lines rather than 6, but, hey, what do you want? You get
the convenience of not typing qualifiers everywhere, and restricted
parametric polymorphism if you ever need it.

> After all, I just want to write two very simple functions, so the effort to
> factor into a type class + two instances, also having to include the Convert
> c in the context whenever I call one of them just seems really painful.
> Also, the word "Convert" is now used up as well...
>
First, you only add the context when the type variable escapes and
gets universally quantified (modulo that class restriction). That is,
you only end up typing anything extra if you use the convert function
in a way which is polymorphic, something that you can't do with your
ad-hoc polymorphism, or with modules. Convert is only used up *as the
name of a typeclass*, which is a pretty sparse namespace to begin
with. Just name the classes after the functions you're generalising,
and you'll run out of names at exactly the same time.

Secondly, if the functions are really different, and you never plan to
use them polymorphically, why the heck would you want to call them the
same thing? That's just confusing to anyone that has to read the code
later. If you end up qualifying all the uses of them with implicitly
declared module names, and all the modules are in (qualified) scope,
that's basically the same as adding qualifications to the names in the
first place.

It's just as bad to end up with a bunch of almost-empty namespaces as
it is to end up with one large too-cluttered namespace.

> Also, when I'm just writing code in an exploratory way, I don't know in
> advance what the common things are that could be factored out into type
> classes (except perhaps in very simple examples like that above), so while
> I'm writing the code for the first time there is still a problem trying to
> think up different names.

Perhaps a thesaurus? :) I'm sorry, but I've always had a really hard
time taking this sort of complaint seriously. You *really* can't come
up with a unique name? Try adding an adjective, or if that's too much
typing, even a (meaningful) single letter prefix/postfix. Some people
hate those, but I see no problem with them when used sparingly. In the
case of different tree types, you can name one of them RoseTree, and
another BinaryTree.

If it's still really hard to come up with a name, possibly you
shouldn't be naming the thing in the first place. Rather than trying
to keep your fingers moving, think about the design a moment longer.
Why are you defining it? What function does it serve to the program?
Programming in Haskell is more about contemplation of a problem than
lots of typing anyway.

 - Cale


More information about the Haskell-Cafe mailing list