Programming-Guidelines.txt revision ce59e0cc5c7221245ed323290bfccbda4ee32dd9
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingProgramming guidelines shall help to make the code of a project better
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingreadable and maintainable by the varying number of contributors.
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingIt takes some programming experience to develop something like a
2d2eda71267231c2526be701fe655db125852c1ffieldingpersonal "coding style" and guidelines only serve as rough shape
2d2eda71267231c2526be701fe655db125852c1ffieldingfor code. Guidelines should be followed by all members working on the
2d2eda71267231c2526be701fe655db125852c1ffieldingproject even if they prefer (or are already used to) different
2d2eda71267231c2526be701fe655db125852c1ffieldingIn the following I will describe documentation, file format, naming
2d2eda71267231c2526be701fe655db125852c1ffieldingconventions and good programming practice (adapted form Matt's C/C++
2d2eda71267231c2526be701fe655db125852c1ffieldingProgramming Guidelines and the Linux kernel coding style).
2d2eda71267231c2526be701fe655db125852c1ffielding---------------------------------------------------------------------------
2d2eda71267231c2526be701fe655db125852c1ffieldingDocumentation:
2d2eda71267231c2526be701fe655db125852c1ffielding---------------------------------------------------------------------------
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingComments are to be written in application terms (i.e. user's point of
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingview). Don't use technical terms - that's what the code is for!
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingComments should be written using correct spelling and grammar in complete
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingsentences with punctation (in English only).
f062ed7bd262a37a909dd77ce5fc23b446818823fielding"Generally, you want your comments to tell WHAT your code does, not HOW.
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingAlso, try to avoid putting comments inside a function body: if the
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingfunction is so complex that you need to separately comment parts of it,
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingyou should probably" (see "Good Programming Practice")
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingPut a haddock comment on top of every exported function and data type!
64185f9824e42f21ca7b9ae6c004484215c031a7rbb---------------------------------------------------------------------------
2d2eda71267231c2526be701fe655db125852c1ffieldingFile Format:
f062ed7bd262a37a909dd77ce5fc23b446818823fielding---------------------------------------------------------------------------
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingAll Haskell source files start with a haddock header of the form:
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingModule : $Header$
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingCopyright : (c) <You> and Uni Bremen 2005
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingLicence : similar to LGPL, see HetCATS/LICENCE.txt or LIZENZ.txt
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingMaintainer : maeder@tzi.de
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingStability : provisional
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingPortability : portable
2d2eda71267231c2526be701fe655db125852c1ffielding <Description>
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingA possible compiler pragma (like {-# OPTIONS -cpp #-}) may precede
2d2eda71267231c2526be701fe655db125852c1ffieldingthis header. The following hierarchical module name must of course
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingmatch the file name.
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingMake sure that the Description is changed to meet the module (if the
2d2eda71267231c2526be701fe655db125852c1ffieldingheader was copied from elsewhere). Insert your email address as maintainer.
2d2eda71267231c2526be701fe655db125852c1ffieldingTry to write portable (Haskell98) code. If you (indirectly) import
2d2eda71267231c2526be701fe655db125852c1ffieldingLogic/Logic.hs the code becomes "non-portable".
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbbThe Dollar-Header-Dollar entry is automatically expanded by cvs (and will wrap
b980ad7fdc218b4855cde9f75a747527f50c554dwrowearound). All other lines should not be longer than 80 (preferably 75)
ab5581cc78e9d865b0a6ab1404c53347b3276968rbbcharacters to avoid wrapped lines (for casual readers)!
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbbExpand all your tabs to spaces to avoid the danger of wrongly expanding
c9a95767fbf0f5fb0976a06b97a256033925e433rbbthem (or a different display of tabs versus eight spaces). Possibly put
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fieldingsomething like the following in your ~/.emacs file.
2d2eda71267231c2526be701fe655db125852c1ffielding (custom-set-variables '(indent-tabs-mode nil))
2d2eda71267231c2526be701fe655db125852c1ffieldingThe last character in your file should be a newline! Under solaris
61fd0cab072a05b855cbef9c585702401ac5ae29rbbyou'll get a warning if this is not the case and sometimes last lines
61fd0cab072a05b855cbef9c585702401ac5ae29rbbwithout newlines are ignored (i.e. "#endif" without newline). Emacs
61fd0cab072a05b855cbef9c585702401ac5ae29rbbusually asks for a final newline.
2d2eda71267231c2526be701fe655db125852c1ffieldingThe whole module should not be too long (about 400 lines)
2d2eda71267231c2526be701fe655db125852c1ffielding---------------------------------------------------------------------------
2d2eda71267231c2526be701fe655db125852c1ffieldingNaming Conventions:
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbIn Haskell types start with capital and functions with lowercase
61fd0cab072a05b855cbef9c585702401ac5ae29rbbletters, so only avoid infix identifiers! (i.e. "\\" causes preprocessor
61fd0cab072a05b855cbef9c585702401ac5ae29rbbproblems and DrIFT has some limitation with infix constructors)
2d2eda71267231c2526be701fe655db125852c1ffieldingNames (especially global ones) should be descriptive and if you need
bfb62a96023822c56c9120e4ee627d4091cc59c2rbblong names write them with underlines and lowercase letters or as
bfb62a96023822c56c9120e4ee627d4091cc59c2rbbmixed case words. (but "tmp" is to be preferred over
61fd0cab072a05b855cbef9c585702401ac5ae29rbb"thisVariableIsATemporaryCounter")
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbGood Programming Practice:
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
2d2eda71267231c2526be701fe655db125852c1ffielding"Functions should be short and sweet, and do just one thing. They should
61fd0cab072a05b855cbef9c585702401ac5ae29rbbfit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
61fd0cab072a05b855cbef9c585702401ac5ae29rbbas we all know), and do one thing and do that well."
61fd0cab072a05b855cbef9c585702401ac5ae29rbbIt's not fixed how deep you indent (4 or 8 chars). You can break the
61fd0cab072a05b855cbef9c585702401ac5ae29rbbline after "do", "let", "where", and "case .. of". Make sure that
61fd0cab072a05b855cbef9c585702401ac5ae29rbbrenamings don't destroy your layout. (If you get to far to the right,
61fd0cab072a05b855cbef9c585702401ac5ae29rbbthe code is unreadable anyway and needs to be decomposed.)
2d2eda71267231c2526be701fe655db125852c1ffieldingcase foo of Foo -> "Foo"
3d96ee83babeec32482c9082c9426340cee8c44dwrowe Bar -> "Bar"
2d2eda71267231c2526be701fe655db125852c1ffieldingcase <longer expression> of
2d2eda71267231c2526be701fe655db125852c1ffielding Foo -> "Foo"
2d2eda71267231c2526be701fe655db125852c1ffielding Bar -> "Bar"
2d2eda71267231c2526be701fe655db125852c1ffieldingAvoid the notation with braces and semicolons since the layout rule
2d2eda71267231c2526be701fe655db125852c1ffieldingforces you to properly align your alternatives.
61fd0cab072a05b855cbef9c585702401ac5ae29rbbRespect compiler warnings. Supply type signatures, avoid shadowing and
61fd0cab072a05b855cbef9c585702401ac5ae29rbbunused variables. Particularly avoid non-exhaustive and
61fd0cab072a05b855cbef9c585702401ac5ae29rbboverlapping patterns. Missing unreachable cases can be filled in using
61fd0cab072a05b855cbef9c585702401ac5ae29rbb"error" with a fixed string "<ModuleName>.<function>" to indicate the
7bdef86e15d47d16dcbe7a5611683191774bd5fbgsteinerror position (in case the impossible should happen). Don't invest
61fd0cab072a05b855cbef9c585702401ac5ae29rbbtime to "show" the offending value, only do this temporarily when
7bdef86e15d47d16dcbe7a5611683191774bd5fbgsteindebugging the code.
61fd0cab072a05b855cbef9c585702401ac5ae29rbbDon't leave unused or commented out code in your files!
61fd0cab072a05b855cbef9c585702401ac5ae29rbbCase expressions
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
3d96ee83babeec32482c9082c9426340cee8c44dwrowePrefer case expressions over pattern binding declarations.
61fd0cab072a05b855cbef9c585702401ac5ae29rbbNot always nice:
61fd0cab072a05b855cbef9c585702401ac5ae29rbblongFunctionName (Foo: _ : _) = e1
61fd0cab072a05b855cbef9c585702401ac5ae29rbblongFunctionName (Bar: _) = e2
61fd0cab072a05b855cbef9c585702401ac5ae29rbbBetter (I think):
61fd0cab072a05b855cbef9c585702401ac5ae29rbblongFunctionName arg = case arg of
61fd0cab072a05b855cbef9c585702401ac5ae29rbb Foo : _ : _ -> e1
61fd0cab072a05b855cbef9c585702401ac5ae29rbb Bar : _ -> e2
c9a95767fbf0f5fb0976a06b97a256033925e433rbbFor partial functions document their preconditions (if not obvious)
c9a95767fbf0f5fb0976a06b97a256033925e433rbband make sure that partial functions are only called when
c9a95767fbf0f5fb0976a06b97a256033925e433rbbpreconditions are obviously fulfilled (i.e. by a case statement or a
c9a95767fbf0f5fb0976a06b97a256033925e433rbbprevious test). Particularly the call of "head" should be used with
c9a95767fbf0f5fb0976a06b97a256033925e433rbbcare or avoided by a case statement.
c9a95767fbf0f5fb0976a06b97a256033925e433rbbAvoid mixing "let" and "where". (I prefer "let" and have auxiliary
c9a95767fbf0f5fb0976a06b97a256033925e433rbbfunction on the top-level that are not exported.) Export lists also
c9a95767fbf0f5fb0976a06b97a256033925e433rbbsupport the detection of unused functions.
c9a95767fbf0f5fb0976a06b97a256033925e433rbbIf you notice that ypu're doing the same task again, try to generalize
c9a95767fbf0f5fb0976a06b97a256033925e433rbbyour first one in order to avoid duplicate code. It is
c9a95767fbf0f5fb0976a06b97a256033925e433rbbfrustrating to change the same error in several places.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbFor (large) records avoid to use of the constructor directly and consider
61fd0cab072a05b855cbef9c585702401ac5ae29rbbthat the order and number of fields may change.
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbTry to strictly separate IO, Monad and pure (without do) function
61fd0cab072a05b855cbef9c585702401ac5ae29rbbprogramming (possibly via different modules).
3d96ee83babeec32482c9082c9426340cee8c44dwrowe x <- return y
7bdef86e15d47d16dcbe7a5611683191774bd5fbgsteinList Comprehensions
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbUse these only when "short and sweet". (I prefer map, filter, and foldr.)
61fd0cab072a05b855cbef9c585702401ac5ae29rbb---------------------------------------------------------------------------
61fd0cab072a05b855cbef9c585702401ac5ae29rbbTracing is for debugging purposes only and should not be used as
3d96ee83babeec32482c9082c9426340cee8c44dwrowefeedback for the user. Clean code is not cluttered by trace calls.
2d2eda71267231c2526be701fe655db125852c1ffieldingApplication notation
2d2eda71267231c2526be701fe655db125852c1ffielding---------------------------------------------------------------------------
2d2eda71267231c2526be701fe655db125852c1ffieldingMany parentheses can be avoid using the infix application operator "$"
2d2eda71267231c2526be701fe655db125852c1ffieldingwith lowest priority. Try at least to avoid unnecessary parentheses in
2d2eda71267231c2526be701fe655db125852c1ffieldingstandard infix expression.
2d2eda71267231c2526be701fe655db125852c1ffieldingf x : g x ++ h x
2d2eda71267231c2526be701fe655db125852c1ffieldinga == 1 && b == 1 || a == 0 && b == 0
61fd0cab072a05b855cbef9c585702401ac5ae29rbbRather than putting a large final argument in parentheses (with a
61fd0cab072a05b855cbef9c585702401ac5ae29rbbdistant closing one) consider using "$" instead.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb"f (g x)" becomes "f $ g x" and consecutive applications
61fd0cab072a05b855cbef9c585702401ac5ae29rbb"f (g (h x))" can be written as "f $ g $ h x" or "f . g $ h x".
61fd0cab072a05b855cbef9c585702401ac5ae29rbbA function definition like
61fd0cab072a05b855cbef9c585702401ac5ae29rbb"f x = g $ h x" can be abbreviated to "f = g . h".
3d96ee83babeec32482c9082c9426340cee8c44dwroweNote that the final argument may even be an infix- or case expression:
7bdef86e15d47d16dcbe7a5611683191774bd5fbgsteinmap id $ c : l
61fd0cab072a05b855cbef9c585702401ac5ae29rbbfilter (const True) . map id $ case l of ...
61fd0cab072a05b855cbef9c585702401ac5ae29rbbHowever, be aware that $-terms cannot be composed further in infix
61fd0cab072a05b855cbef9c585702401ac5ae29rbbexpressions.
61fd0cab072a05b855cbef9c585702401ac5ae29rbbProbably wrong:
61fd0cab072a05b855cbef9c585702401ac5ae29rbbf $ x ++ g $ x
61fd0cab072a05b855cbef9c585702401ac5ae29rbbBut the scope of an expression is also limited by the layout rule, so
3d96ee83babeec32482c9082c9426340cee8c44dwroweit is usually save to use "$" on right hand sides.
2d2eda71267231c2526be701fe655db125852c1ffielding do y <- f $ l
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar do y <- g $ l
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarLast warning: always leave spaces around "$" (and other mixfix
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coaroperators) since a clash with template haskell is possible.
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar(Also write "\ t" instead of "\t" in lambda expressions)
3d96ee83babeec32482c9082c9426340cee8c44dwrowe---------------------------------------------------------------------------
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarPrefer proper data types over type synonyms or tupels even if you have
3d96ee83babeec32482c9082c9426340cee8c44dwroweto do more constructing and unpacking. This will make it easier to
a19698aebe10b9d41574e4a73794ba7d4cecd78btrawicksupply class instances later on. Don't put class constraints on
a19698aebe10b9d41574e4a73794ba7d4cecd78btrawicka data type, constraints belong only to the functions that manipulate
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarUsing type synonyms consistently is difficult over a longer time,
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarbecause this is not checked by the compiler.
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar---------------------------------------------------------------------------
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarStandard library modules like Char. List, Maybe, Monad, etc should be
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarimported by their hierarchical module name:
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarThe libraries for Set, Map and Rel are to imported qualified:
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarimport qualified Common.Lib.Set as Set
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarimport qualified Common.Lib.Map as Map
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarimport qualified Common.Lib.Rel as Rel
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarGlasgow extensions and Classes
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar---------------------------------------------------------------------------
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarStay away form extensions as long as possible. Also use classes with
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarcare because soon the desire for overlapping instances (like for lists
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarand strings) may arise. Then you may want MPTC (multi-parameter type
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarclasses), functional dependencies, undecidable and possibly incoherent
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarinstances and then you are "in the wild" (SPJ).
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar---------------------------------------------------------------------------
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarFinal remarks:
3d96ee83babeec32482c9082c9426340cee8c44dwrowe---------------------------------------------------------------------------
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarDespite guidelines writing "correct code" (without formal proof
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarsupport yet) still remains the major challenge. As motivation to
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarfollow these guidelines consider the points that are
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarfrom the "C++ Coding Standard", where I replaced "C++" with "Haskell".
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coarGood Points:
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar * programmers can go into any code and figure out what's going on
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar * new people can get up to speed quickly
2d2eda71267231c2526be701fe655db125852c1ffielding * people new to Haskell are spared the need to develop a personal
2d2eda71267231c2526be701fe655db125852c1ffielding style and defend it to the death
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * people new to Haskell are spared making the same mistakes over
61fd0cab072a05b855cbef9c585702401ac5ae29rbb and over again
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * people make fewer mistakes in consistent environments
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * programmers have a common enemy :-)
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * the standard is usually stupid because it was made by someone
61fd0cab072a05b855cbef9c585702401ac5ae29rbb who doesn't understand Haskell
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * the standard is usually stupid because it's not what I do
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * standards reduce creativity
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * standards are unnecessary as long as people are consistent
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * standards enforce too much structure
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * people ignore standards anyway
61fd0cab072a05b855cbef9c585702401ac5ae29rbb(Send comments to the "Maintainer" of this file)