Personal tools

Qualified names

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(moved from Hawiki)
 
("to" vs. "from")
 
(3 intermediate revisions by one user not shown)
Line 1: Line 1:
 
http://www.dcs.gla.ac.uk/mail-www/haskell/msg02004.html
 
http://www.dcs.gla.ac.uk/mail-www/haskell/msg02004.html
   
{{{
+
Please make more use of the qualified ID syntax!
From: "Frank A. Christoph" <christo@nextsolution.co.jp>
+
Haskell has had this feature for quite some time now,
Subject: Qualified identifiers (was: RE: What *I* thinks Haskell Needs.)
+
but I hardly ever see it used ...
To: <haskell@haskell.org>
 
Date: Mon, 27 Sep 1999 18:12:40 +0900
 
 
> For bigger things which
 
> you want to identify, you can either use qualified module identifiers, or
 
> Haskell's type classes which at least let you establish and codify a
 
> motivation for using the same name for distinct things (namely, that their
 
> types are equivalent under a particular relation).
 
 
BTW, since I mentioned it, let me get on my hobby horse and implore other
 
Haskell programmers to ''please'' make more use of the qualified ID syntax!
 
Haskell has had this feature for quite some time now, but I hardly ever see
 
it used...
 
   
 
Especially in modules that import a lot of other modules, it's
 
Especially in modules that import a lot of other modules, it's
   
* easier to ''recognize'' an identifier if it's prefixed by a module ID
+
* easier to ''recognize'' an identifier if it's prefixed by a module ID rather than look through all the imported modules, and
rather than look through all the imported modules, and
+
* easier to ''modify'' a module if all you need to do to pull in a new value is to use it, rather than scrolling up, and adding it to the list of identifiers in something like <hask>import M (...)</hask>.
 
* easier to ''modify'' a module if all you need to do to pull in a new value
 
is to use it, rather than scrolling up, and adding it to the list of
 
identifiers in something like "`import M (...)`".
 
   
This is standard practice in both SML and Ocaml. (In fact, I would rather
+
This is standard practice in both SML and Ocaml.
not have to declare things like "`import qualified M`" at all...)
+
(In fact, I would rather not have to declare things like <hask>import qualified M</hask> at all ...)
   
 
If you have a long module name, declare an alias:
 
If you have a long module name, declare an alias:
   
`import qualified LongModuleName as LMN`
+
<hask>import qualified LongModuleName as LMN</hask>
   
 
BTW, another advantage of this syntax is that identifiers within their own
 
BTW, another advantage of this syntax is that identifiers within their own
defining module get shorter, and consequently it gets easier to read. For
+
defining module get shorter, and consequently it gets easier to read.
example, don't define "`addToFM`"; define "`addTo`" and then do
+
For example, don't define <hask>addToFM</hask>;
"`import FiniteMap as FM`". Sometimes this means having to hide Prelude identifiers
+
define <hask>addTo</hask> and
and qualify them at use (as it would be with "`lookupFM`" in GHC's `FiniteMap`
+
then do <hask>import FiniteMap as FM</hask>.
module) but is that such a great price to pay...?
+
Sometimes this means having to hide Prelude identifiers
  +
and qualify them at use
  +
(as it would be with <hask>lookupFM</hask>
  +
in GHC's <hask>FiniteMap</hask> module)
  +
but is that such a great price to pay...?
   
 
Please. Pretty please? Pretty please with sugar on top?
 
Please. Pretty please? Pretty please with sugar on top?
Line 29: Line 29:
   
 
P.S. Except infix combinators. These are ugly when you qualify them.
 
P.S. Except infix combinators. These are ugly when you qualify them.
}}}
 
   
 
----
 
----
   
One good example of qualified names, one of the Base64 modules has functions named encode and decode. Those are the most sensible names, but are very likely to clash or shadow other functions if directly imported. If you {{{import qualified Base64}}} then you can write Base64.decode and everyone's happy.
+
One good example of qualified names, one of the Base64 modules has functions named encode and decode. Those are the most sensible names, but are very likely to clash or shadow other functions if directly imported. If you <hask>import qualified Base64</hask> then you can write Base64.decode and everyone's happy.
   
 
----
 
----
Line 40: Line 39:
 
In the style of Modula-3 I define one data type or one type class per module.
 
In the style of Modula-3 I define one data type or one type class per module.
 
The module is named after the implemented type or class.
 
The module is named after the implemented type or class.
Then a type is named {{{T}}}, and a type class {{{C}}}.
+
Then a type is named <hask>T</hask>, and a type class <hask>C</hask>.
I use them qualified, e.g. {{{Music.T}}} or {{{Collection.C}}}.
+
I use them qualified, e.g. <hask>Music.T</hask> or <hask>Collection.C</hask>.
Similarly, if a type has only one constructor then I call it {{{Cons}}} and use it qualified {{{MidiFile.Cons}}}.
+
Similarly, if a type has only one constructor then I call it <hask>Cons</hask> and use it qualified <hask>MidiFile.Cons</hask>.
 
This style also answers the annoying question whether the module name should be in singular or plural form:
 
This style also answers the annoying question whether the module name should be in singular or plural form:
 
Always choose singular form!
 
Always choose singular form!
   
{{{Cons}}} is going to confuse the heck out of those like myself who still think in Lisp sometimes. Would {{{Con}}} be ok instead? -- BartMassey
+
:<hask>Cons</hask> is going to confuse the heck out of those like myself who still think in Lisp sometimes. Would <hask>Con</hask> be ok instead? -- BartMassey
   
I thought the similarity to LISP is good. -- HenningThielemann
+
::I thought the similarity to LISP is good. -- HenningThielemann
   
 
Many functions can be considered as conversions.
 
Many functions can be considered as conversions.
So I define functions like {{{Music.toMIDI}}} and {{{Music.fromMIDI}}} and use it in this qualified form.
+
So I define functions like <hask>Music.toMIDI</hask> and <hask>Music.fromMIDI</hask> and use it in this qualified form.
 
This way I can import all modules qualified, avoiding name clashes and I can provide a consistent naming through all modules.
 
This way I can import all modules qualified, avoiding name clashes and I can provide a consistent naming through all modules.
  +
Since Haskell's direction of processing in functions (except monad notation) is from right-to-left,
  +
the "from" names are preferable.
  +
The expression <hask>a = A.fromB b</hask> is certainly nicer than <hask>a = B.toA b</hask>.
  +
However, the order of functions and their arguments is subject of
  +
[http://www.haskell.org/pipermail/libraries/2006-October/006079.html criticism].
   
 
The big question remains: In which module shall the conversions reside?
 
The big question remains: In which module shall the conversions reside?
Translated to our example: Shall it be {{{Music.toMIDI}}} or {{{MIDI.fromMusic}}}?
+
Translated to our example: Shall it be <hask>Music.toMIDI</hask> or <hask>MIDI.fromMusic</hask>?
 
I suggest the following: Find out which module is the general one, and which is the more specific one.
 
I suggest the following: Find out which module is the general one, and which is the more specific one.
 
I consider the more specific module as an extension to the general module.
 
I consider the more specific module as an extension to the general module.
Line 61: Line 65:
 
That's why you should put all conversions into the extension modules.
 
That's why you should put all conversions into the extension modules.
 
How to find out which module is more specific?
 
How to find out which module is more specific?
Imagine module A and module B still don't contain any conversion routine between {{{A.T}}} and {{{B.T}}}.
+
Imagine module A and module B still don't contain any conversion routine between <hask>A.T</hask> and <hask>B.T</hask>.
 
If module B imports A, then B is the more specific one. Put all conversions between A and B into B.
 
If module B imports A, then B is the more specific one. Put all conversions between A and B into B.
 
If module A and B are mutually recursive or don't import each other,
 
If module A and B are mutually recursive or don't import each other,
 
then rethink if one or the other is the more specific one.
 
then rethink if one or the other is the more specific one.
If they are on the same level of generality you may add a new module dedicated to conversion between {{{A.T}}} and {{{B.T}}}.
+
If they are on the same level of generality you may add a new module dedicated to conversion between <hask>A.T</hask> and <hask>B.T</hask>.
   
 
-- HenningThielemann
 
-- HenningThielemann

Latest revision as of 13:49, 22 November 2006

http://www.dcs.gla.ac.uk/mail-www/haskell/msg02004.html

Please make more use of the qualified ID syntax! Haskell has had this feature for quite some time now, but I hardly ever see it used ...

Especially in modules that import a lot of other modules, it's

  • easier to recognize an identifier if it's prefixed by a module ID rather than look through all the imported modules, and
  • easier to modify a module if all you need to do to pull in a new value is to use it, rather than scrolling up, and adding it to the list of identifiers in something like
    import M (...)
    .

This is standard practice in both SML and Ocaml.

(In fact, I would rather not have to declare things like
import qualified M
at all ...)

If you have a long module name, declare an alias:

import qualified LongModuleName as LMN

BTW, another advantage of this syntax is that identifiers within their own defining module get shorter, and consequently it gets easier to read.

For example, don't define
addToFM
; define
addTo
and then do
import FiniteMap as FM
.

Sometimes this means having to hide Prelude identifiers and qualify them at use

(as it would be with
lookupFM
in GHC's
FiniteMap
module)

but is that such a great price to pay...?

Please. Pretty please? Pretty please with sugar on top?

--FC

P.S. Except infix combinators. These are ugly when you qualify them.


One good example of qualified names, one of the Base64 modules has functions named encode and decode. Those are the most sensible names, but are very likely to clash or shadow other functions if directly imported. If you
import qualified Base64
then you can write Base64.decode and everyone's happy.

I use this extensively in an even more rigorous way. In the style of Modula-3 I define one data type or one type class per module. The module is named after the implemented type or class.

Then a type is named
T
, and a type class
C
. I use them qualified, e.g.
Music.T
or
Collection.C
. Similarly, if a type has only one constructor then I call it
Cons
and use it qualified
MidiFile.Cons
.

This style also answers the annoying question whether the module name should be in singular or plural form: Always choose singular form!

Cons
is going to confuse the heck out of those like myself who still think in Lisp sometimes. Would
Con
be ok instead? -- BartMassey
I thought the similarity to LISP is good. -- HenningThielemann

Many functions can be considered as conversions.

So I define functions like
Music.toMIDI
and
Music.fromMIDI
and use it in this qualified form.

This way I can import all modules qualified, avoiding name clashes and I can provide a consistent naming through all modules. Since Haskell's direction of processing in functions (except monad notation) is from right-to-left, the "from" names are preferable.

The expression
a = A.fromB b
is certainly nicer than
a = B.toA b
.

However, the order of functions and their arguments is subject of criticism.

The big question remains: In which module shall the conversions reside?

Translated to our example: Shall it be
Music.toMIDI
or
MIDI.fromMusic
?

I suggest the following: Find out which module is the general one, and which is the more specific one. I consider the more specific module as an extension to the general module. When you write a general module you cannot predict all possible extensions. That's why you should put all conversions into the extension modules. How to find out which module is more specific?

Imagine module A and module B still don't contain any conversion routine between
A.T
and
B.T
.

If module B imports A, then B is the more specific one. Put all conversions between A and B into B. If module A and B are mutually recursive or don't import each other, then rethink if one or the other is the more specific one.

If they are on the same level of generality you may add a new module dedicated to conversion between
A.T
and
B.T
.

-- HenningThielemann