Programming languages have different types of '''means of expression''':

Programming languages have different types of '''means of expression''':

−

* Primary means of expression: variables, types, parentheses, etc. in general all things that are relevant to the compiler

+

* Primary means of expression: variables, types, parentheses, modules, etc. in general all things that are relevant to the compiler

−

* Secondary means of expression: layout, comments, etc. that is language elements that are irrelevant for the compiler, and thus are made exclusively for the human reader

+

* Secondary means of expression: language irrelevant layout and spacing, comments, conventions, etc. that is language elements that are irrelevant for the compiler, and thus are made exclusively for the human reader

−

In Haskell (as well as in Python) one has to weaken that because layout is interpreted by the compiler.

+

It is generally good style to remember the rule:

It is generally good style to remember the rule:

Line 12:

Line 11:

== Examples ==

== Examples ==

−

=== Expressive variable names instead of comments ===

+

=== Expressive names instead of comments ===

I recently saw code like

I recently saw code like

Line 30:

Line 29:

</haskell>

</haskell>

−

=== Variables instead of comments ===

+

=== Functions instead of comments ===

<haskell>

<haskell>

Line 65:

Line 64:

foo :: [a] -> Maybe a

foo :: [a] -> Maybe a

</haskell>

</haskell>

+

+

=== Proper types instead of special values ===

+

+

In languages like C you often see declarations like

+

<code>

+

/*

+

This function returns the number of objects

+

or -1 if there was an I/O error.

+

*/

+

int count (description d);

+

</code>

+

It is very easy to miss an error indicated by the <code>count</code> function

+

and the language let you do arithmetic even with the error indicating value <code>-1</code> as in

+

<code>

+

c = count(d0)+count(d1);

+

</code>

+

But C only allows one return value per function.

+

Further results have to be written to a pointer argument (cumbersome)

+

or to a global variable (easy to miss, too, and not thread-safe).

+

Haskell's type system supports such situations much better

+

and you should use this support.

+

Try hard to avoid values that have a special meaning

+

by using appropriate types for indicating special conditions as in

+

<haskell>

+

count :: Description -> IO (Maybe Int)

+

</haskell>

+

+

=== Qualification instead of identifier prefix/suffix conventions ===

+

+

You may define

+

<haskell>

+

module File where

+

+

openFile :: FilePath -> IO Handle

+

</haskell>

+

and intend to import that unqualified

+

<haskell>

+

import File

+

+

main :: IO ()

+

main = do

+

h <- openFile "myfile"

+

...

+

</haskell>

+

in other modules, or you can define

+

<haskell>

+

module File where

+

+

open :: FilePath -> IO Handle

+

</haskell>

+

that would fit for qualified import as in

+

<haskell>

+

import qualified File

+

+

main :: IO ()

+

main = do

+

h <- File.open "myfile"

+

...

+

</haskell>

+

+

The second way would use primary means of the language

+

(modules and qualified imports)

+

whereas the first way uses secondary means of the language

+

(naming convention).

+

The naming convention cannot be checked,

+

that is <hask>fileOpen</hask> and <hask>openFile</hask>

+

would be accepted by any Haskell compiler

+

and it forces the user to follow this convention,

+

leading to respect different conventions from different packages

+

that are imported in one module,

+

e.g. type name in the identifier as prefix vs. suffix.

+

Using qualified imports,

+

the importer can choose an abbreviation that he likes,

+

say:

+

<haskell>

+

import qualified File as F

+

+

main :: IO ()

+

main = do

+

h <- F.open "myfile"

+

...

+

</haskell>

+

and thus assert a consistent naming style in his module.

+

+

Find out about more style questions on [[Import modules properly|importing]] and [[qualified names]].

== See also ==

== See also ==

Latest revision as of 23:26, 14 March 2011

Programming languages have different types of means of expression:

Primary means of expression: variables, types, parentheses, modules, etc. in general all things that are relevant to the compiler

Secondary means of expression: language irrelevant layout and spacing, comments, conventions, etc. that is language elements that are irrelevant for the compiler, and thus are made exclusively for the human reader

It is generally good style to remember the rule:

Prefer primary means of expression to secondary ones!

The reason is, that elements that are relevant to the compiler are checked by the compiler and can be processed by documentation, analysis and refactoring tools.
Thus it is not good style to comment intensively if the language provides primary ways to express our ideas.

It was hard to read the program because there were several other two-character function names to remember,
and the comment was only attached to the function definition, not the calls.
The comments would be better replaced by expressive function names like

It is likely that logical units like the 2x2 determinant are reused later (e.g. for the vector product)
or that they should be tested separately.
Thus it is better to factor them out into separate functions.

/*
This function returns the number of objects
or -1 if there was an I/O error.
*/
int count (description d);

It is very easy to miss an error indicated by the count function
and the language let you do arithmetic even with the error indicating value -1 as in

c = count(d0)+count(d1);

But C only allows one return value per function.
Further results have to be written to a pointer argument (cumbersome)
or to a global variable (easy to miss, too, and not thread-safe).
Haskell's type system supports such situations much better
and you should use this support.
Try hard to avoid values that have a special meaning
by using appropriate types for indicating special conditions as in

The second way would use primary means of the language
(modules and qualified imports)
whereas the first way uses secondary means of the language
(naming convention).
The naming convention cannot be checked,

that is

fileOpen

and

openFile

would be accepted by any Haskell compiler
and it forces the user to follow this convention,
leading to respect different conventions from different packages
that are imported in one module,
e.g. type name in the identifier as prefix vs. suffix.
Using qualified imports,
the importer can choose an abbreviation that he likes,
say: