Programming-Guidelines.txt revision 798a3d6fdcb8c17b0bc3502a150be75c9ec8799b
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederProgramming guidelines shall help to make the code of a project better
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederreadable and maintainable by the varying number of contributors.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIt takes some programming experience to develop something like a
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederpersonal "coding style" and guidelines only serve as rough shape
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederfor code. Guidelines should be followed by all members working on the
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederproject even if they prefer (or are already used to) different
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederguidelines.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIn the following I will describe documentation, file format, naming
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederconventions and good programming practice (adapted form Matt's C/C++
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederProgramming Guidelines and the Linux kernel coding style).
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederDocumentation:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederComments are to be written in application terms (i.e. user's point of
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederview). Don't use technical terms - that's what the code is for!
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederComments should be written using correct spelling and grammar in complete
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedersentences with punctation (in English only).
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"Generally, you want your comments to tell WHAT your code does, not HOW.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederAlso, try to avoid putting comments inside a function body: if the
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederfunction is so complex that you need to separately comment parts of it,
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederyou should probably" (see "Good Programming Practice")
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederPut a haddock comment on top of every exported function and data type!
96de7ec4008f75574077816c4c71a22e6afe1e01Christian MaederMake sure haddock accepts these comments by "make doc" after full
96de7ec4008f75574077816c4c71a22e6afe1e01Christian Maedercompilation with "make all".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederFile Format:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederAll Haskell source files start with a haddock header of the form:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder{- |
9d3c461220f8076ef80ca48f7b0574ded9b23e7aChristian MaederModule : <File name, i.e. generated by \$Header\$>
9d3c461220f8076ef80ca48f7b0574ded9b23e7aChristian MaederDescription : <Short text displayed on contents page>
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederCopyright : (c) <You> and Uni Bremen 2005
162a689da386fc8ddbbe47bcae83eaca4fc8dbc0Christian MaederLicense : similar to LGPL, see HetCATS/LICENSE.txt or LIZENZ.txt
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederMaintainer : maeder@tzi.de
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederStability : provisional
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederPortability : portable
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
9d3c461220f8076ef80ca48f7b0574ded9b23e7aChristian Maeder<module description starting at first column>
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder-}
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederA possible compiler pragma (like {-# OPTIONS -cpp #-}) may precede
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maederthis header. The following hierarchical module name must of course
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maedermatch the file name.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederMake sure that the description is changed to meet the module (if the
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederheader was copied from elsewhere). Insert your email address as maintainer.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederTry to write portable (Haskell98) code. If you (indirectly) import
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederLogic/Logic.hs (with multi-parameter type classes and functional
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederdependencies) the code becomes "non-portable".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederThe Dollar-Header-Dollar entry is automatically expanded by cvs (and will wrap
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederaround). All other lines should not be longer than 80 (preferably 75)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedercharacters to avoid wrapped lines (for casual readers)!
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederExpand all your tabs to spaces to avoid the danger of wrongly expanding
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederthem (or a different display of tabs versus eight spaces). Possibly put
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedersomething like the following in your ~/.emacs file.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder (custom-set-variables '(indent-tabs-mode nil))
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederThe last character in your file should be a newline! Under solaris
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederyou'll get a warning if this is not the case and sometimes last lines
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederwithout newlines are ignored (i.e. "#endif" without newline). Emacs
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederusually asks for a final newline.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederThe whole module should not be too long (about 400 lines)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNaming Conventions:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIn Haskell types start with capital and functions with lowercase
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederletters, so only avoid infix identifiers! (i.e. "\\" at the end of a
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederline causes preprocessor problems and DrIFT has some limitation with
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederinfix constructors)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNames (especially global ones) should be descriptive and if you need
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederlong names write them with underlines and lowercase letters or as
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedermixed case words. (but "tmp" is to be preferred over
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"thisVariableIsATemporaryCounter")
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederGood Programming Practice:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"Functions should be short and sweet, and do just one thing. They should
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederfit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederas we all know), and do one thing and do that well."
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIt's not fixed how deep you indent (4 or 8 chars). You can break the
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederline after "do", "let", "where", and "case .. of". Make sure that
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederrenamings don't destroy your layout. (If you get to far to the right,
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederthe code is unreadable anyway and needs to be decomposed.)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederBad:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder case foo of Foo -> "Foo"
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder Bar -> "Bar"
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederGood:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder case <longer expression> of
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Foo -> "Foo"
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Bar -> "Bar"
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederAvoid the notation with braces and semicolons since the layout rule
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederforces you to properly align your alternatives.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederRespect compiler warnings. Supply type signatures, avoid shadowing and
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederunused variables. Particularly avoid non-exhaustive and
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederoverlapping patterns. Missing unreachable cases can be filled in using
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"error" with a fixed string "<ModuleName>.<function>" to indicate the
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedererror position (in case the impossible should happen). Don't invest
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedertime to "show" the offending value, only do this temporarily when
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederdebugging the code.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederDon't leave unused or commented-out code in your files!
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederCase expressions
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederPrefer case expressions over pattern binding declarations.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNot always nice:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder longFunctionName (Foo: _ : _) = e1
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder longFunctionName (Bar: _) = e2
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederBetter (I think):
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder longFunctionName arg = case arg of
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Foo : _ : _ -> e1
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Bar : _ -> e2
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder _ -> error "Programming-Guidelines.longFunctionName"
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederFor partial functions document their preconditions (if not obvious)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederand make sure that partial functions are only called when
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederpreconditions are obviously fulfilled (i.e. by a case statement or a
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederprevious test). Particularly the call of "head" should be used with
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maedercare or (even better) be made obsolete by a case statement.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian MaederUsually a case statement (and the import of isJust and formJust from
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian MaederData.Maybe) can be avoided by using the "maybe" function:
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder maybe (error "<ModuleName>.<function>") id $ Map.lookup key map
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian MaederDo avoid mixing "let" and "where". (I prefer "let" and have auxiliary
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederfunction on the top-level that are not exported.) Export lists also
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedersupport the detection of unused functions.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
162a689da386fc8ddbbe47bcae83eaca4fc8dbc0Christian MaederIf you notice that you're doing the same task again, try to generalize
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederit in order to avoid duplicate code. It is frustrating to change the
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maedersame error in several places.
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederApplication notation
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederMany parentheses can be eliminated using the infix application operator "$"
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederwith lowest priority. Try at least to avoid unnecessary parentheses in
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederstandard infix expression.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder f x : g x ++ h x
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder a == 1 && b == 1 || a == 0 && b == 0
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederRather than putting a large final argument in parentheses (with a
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederdistant closing one) consider using "$" instead.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"f (g x)" becomes "f $ g x" and consecutive applications
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"f (g (h x))" can be written as "f $ g $ h x" or "f . g $ h x".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederA function definition like
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"f x = g $ h x" can be abbreviated to "f = g . h".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNote that the final argument may even be an infix- or case expression:
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder map id $ c : l
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder filter (const True) . map id $ case l of ...
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederHowever, be aware that $-terms cannot be composed further in infix
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederexpressions.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederProbably wrong:
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder f $ x ++ g $ x
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederBut the scope of an expression is also limited by the layout rule, so
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederit is usually save to use "$" on right hand sides.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederOk:
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder do y <- f $ l
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder ++
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder do y <- g $ l
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederLast warning: always leave spaces around "$" (and other mixfix
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederoperators) since a clash with template haskell is possible.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder(Also write "\ t" instead of "\t" in lambda expressions)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederList Comprehensions
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder---------------------------------------------------------------------------
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederUse these only when "short and sweet". (I prefer map, filter, and foldr.)
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederTypes
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
162a689da386fc8ddbbe47bcae83eaca4fc8dbc0Christian MaederPrefer proper data types over type synonyms or tuples even if you have
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederto do more constructing and unpacking. This will make it easier to
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedersupply class instances later on. Don't put class constraints on
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedera data type, constraints belong only to the functions that manipulate
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederthe data.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederUsing type synonyms consistently is difficult over a longer time,
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederbecause this is not checked by the compiler.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTake care if your datatype has many variants (unless it is an
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederenumeration type.) Don't repeat common parts in every variant since
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederthis will cause code duplication.
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederBad (to handle arguments in sync):
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Mode f p = Box f p | Diamond f p
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederGood (to handle arguments only once):
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data BoxOrDiamond = Box | Diamond
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Mod f p = Mode BoxOrDiamond f p
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederConsider:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Tupel a b = Tupel a b | Undefined
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederversus:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Tupel a b = Tupel a b
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederand using:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder Maybe (Tupel a b)
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederwhenever an undefined result needs to be propagated
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederRecords
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder---------------------------------------------------------------------------
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederFor (large) records avoid the use of the constructor directly and consider
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederthat the order and number of fields may change.
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTake care with depend polymorphic fields:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Fields a =
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder VariantWithTwo { field1 :: a
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder , field2 :: a }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederThe type of a value v can not be changed by only setting field1:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder v { field1 = f }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederBetter construct a new value:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder VariantWithTwo { field1 = f } -- leaving field2 undefined
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederOr use a polymorphic element that is instantiated by updating:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder e = VariantWithTwo { field1 = [], field2 = [] }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder e { field1 = [f] }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederSeveral variants with identical fields may avoid some code duplication
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederwhen selecting and updating, though possibly not in a few
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederdepended polymorphic cases.
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederHowever, I doubt if the following is a really good alternative:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Mode f p = Box { formula :: f, positions :: p }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder | Diamond { formula :: f, positions :: p }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederIO
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder---------------------------------------------------------------------------
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTry to strictly separate IO, Monad and pure (without do) function
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederprogramming (possibly via different modules).
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederBad:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder x <- return y
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder ...
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederGood:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder let x = y
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder ...
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederDon't use Prelude.interact and make sure your program does not depend
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederon the (not always obvious) order of evaluation. I.e. don't read and
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederwrite to the same file:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederThis will fail:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder do s <- readFile f
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder writeFile f $ 'a' : s
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederbecause of lazy IO! (Writing is starting before reading is finished.)
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTrace
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder---------------------------------------------------------------------------
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTracing is for debugging purposes only and should not be used as
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederfeedback for the user. Clean code is not cluttered by trace calls.
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian MaederImports
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder---------------------------------------------------------------------------
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian MaederStandard library modules like Char. List, Maybe, Monad, etc should be
96de7ec4008f75574077816c4c71a22e6afe1e01Christian Maederimported by their hierarchical module name (so that haddock finds them):
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import Data.List
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import Control.Monad
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import System.Environment
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
96de7ec4008f75574077816c4c71a22e6afe1e01Christian MaederThe libraries for Set, Map and Rel are to be imported qualified:
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import qualified Common.Lib.Set as Set
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import qualified Common.Lib.Map as Map
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder import qualified Common.Lib.Rel as Rel
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederGlasgow extensions and Classes
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder---------------------------------------------------------------------------
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederStay away form extensions as long as possible. Also use classes with
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedercare because soon the desire for overlapping instances (like for lists
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maederand strings) may arise. Then you may want MPTC (multi-parameter type
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maederclasses), functional dependencies, undecidable and possibly incoherent
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederinstances and then you are "in the wild" (according to SPJ).
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder---------------------------------------------------------------------------
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederFinal remarks:
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder---------------------------------------------------------------------------
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederDespite guidelines writing "correct code" (without formal proof
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedersupport yet) still remains the major challenge. As motivation to
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederfollow these guidelines consider the points that are
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maederfrom the "C++ Coding Standard", where I replaced "C++" with "Haskell".
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederGood Points:
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * programmers can go into any code and figure out what's going on
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * new people can get up to speed quickly
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * people new to Haskell are spared the need to develop a personal
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder style and defend it to the death
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * people new to Haskell are spared making the same mistakes over
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder and over again
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * people make fewer mistakes in consistent environments
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * programmers have a common enemy :-)
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederBad Points:
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * the standard is usually stupid because it was made by someone
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder who doesn't understand Haskell
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * the standard is usually stupid because it's not what I do
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * standards reduce creativity
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * standards are unnecessary as long as people are consistent
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * standards enforce too much structure
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maeder * people ignore standards anyway
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder
96de7ec4008f75574077816c4c71a22e6afe1e01Christian MaederSend comments to the "Maintainer" of this file