bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederProgramming guidelines shall help to make the code of a project better
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederreadable and maintainable by the varying number of contributors.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIt takes some programming experience to develop something like a
d71c501e69da3973406558162ad439da2b3464d6Christian Maederpersonal "coding style" and guidelines only serve as rough shape for
d71c501e69da3973406558162ad439da2b3464d6Christian Maedercode. Guidelines should be followed by all members working on the
d71c501e69da3973406558162ad439da2b3464d6Christian Maederproject even if they prefer (or are already used to) different
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederThese guidelines have been originally set up for the hets-project
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder[http://www.informatik.uni-bremen.de/cofi/hets/ hets-project] and are
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedernow put on the [http://haskell.org/haskellwiki/ HaskellWiki] gradually
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederintegrating parts of [http://haskell.org/hawiki the old hawiki]
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maederentries [http://haskell.org/haskellwiki/Things_to_avoid ThingsToAvoid] and
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder[http://haskell.org/hawiki/HaskellStyle HaskellStyle] (hopefully not
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederhurting someone's copyrights). The other related entry
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder[http://haskell.org/hawiki/TipsAndTricks TipsAndTricks] treats more
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederspecific points that are left out here,
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian MaederSurely some style choices are a bit arbitrary (or "religious") and
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedertoo restrictive with respect to language extensions. Nevertheless I hope
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederto keep up these guidelines (at least as a basis) for our project
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederin order to avoid maintaining diverging guidelines. Of course I want
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederto supply - partly tool-dependent - reasons for certain decisions and
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederalso show alternatives by possibly bad examples. At the time of
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederwriting I use ghc-6.4.1, haddock-0.7 and (GNU-) emacs with the latest
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder[http://www.haskell.org/haskell-mode/ haskell mode].
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederThe following quote and links are taken from
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder[http://haskell.org/hawiki/HaskellStyle the old general comments]:
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederWe all have our own ideas about good Haskell style. There's More Than
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederOne Way To Do It. But some ways are better than others.
1d8011d1730fb370a499d59afb3f05be871436feChristian MaederSome comments from the GHC team about their internal coding
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederstandards can be found at
1d8011d1730fb370a499d59afb3f05be871436feChristian Maederhttp://hackage.haskell.org/trac/ghc/wiki/WorkingConventions
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederAlso http://research.microsoft.com/~simonpj/papers/haskell-retrospective/
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedercontains some brief comments on syntax and style,
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederWhat now follows are descriptions of program documentation, file
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederformat, naming conventions and good programming practice (adapted form
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederMatt's C/C++ Programming Guidelines and the Linux kernel coding
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder=== Documentation ===
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 MaederComments should be written using correct spelling and grammar in complete
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maedersentences with punctation (in English only).
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian 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,
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederyou should probably" (... decompose it)
af1cb109bce240bcafe3823df022d6088cbfc438Christian MaederPut a haddock comment on top of every exported function and data type!
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederMake sure haddock accepts these comments.
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder=== File Format ===
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederAll Haskell source files start with a haddock header of the form:
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian MaederModule : <File name or $Header$ to be replaced automatically>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederDescription : <optional short text displayed on contents page>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederCopyright : (c) <Authors or Affiliations>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederLicense : <license>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederMaintainer : <email>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederStability : unstable | experimental | provisional | stable | frozen
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederPortability : portable | non-portable (<reason>)
9d3c461220f8076ef80ca48f7b0574ded9b23e7aChristian Maeder<module description starting at first column>
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederA possible compiler pragma (like {-# LANGUAGE CPP #-}) may precede
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maederthis header. The following hierarchical module name must of course
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian Maedermatch the file name.
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.
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederTry to write portable (Haskell98) code. If you use i.e. multi-parameter
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian Maedertype classes and functional dependencies the code becomes
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian Maeder"non-portable (MPTC with FD)".
b814b1bb510007ba5d0638eb27d93ef93fe64e66Christian MaederThe \$Header\$ entry will be automatically expanded.
b814b1bb510007ba5d0638eb27d93ef93fe64e66Christian MaederLines should not be longer than 80 (preferably 75)
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maedercharacters to avoid wrapped lines (for casual readers)!
b814b1bb510007ba5d0638eb27d93ef93fe64e66Christian MaederDon't leave trailing white space in your code in every line.
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 (custom-set-variables '(indent-tabs-mode nil))
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.
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian MaederYou may use http://hackage.haskell.org/package/scan to check your file format.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederThe whole module should not be too long (about 400 lines)
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder=== Naming Conventions ===
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederIn Haskell types start with capital and functions with lowercase
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederletters, so only avoid infix identifiers! Defining symbolic infix
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederidentifiers should be left to library writers only.
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder(The infix identifier "\\" at the end of a line causes cpp preprocessor
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederNames (especially global ones) should be descriptive and if you need
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederlong names write them as mixed case words (aka camelCase). (but "tmp"
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederis to be preferred over "thisVariableIsATemporaryCounter")
46bf8457679e3dd601af7e35cc0966642ba09794Christian MaederAlso in the standard libraries, function names with multiple words are
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederwritten using the camelCase convention. Similarly, type, typeclass and
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederconstructor names are written using the StudlyCaps convention.
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian MaederSome parts of our code use underlines (without unnecessary uppercase
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederletters) for long identifiers to better reflect names given with
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maederhyphens in the requirement documentation. Also such names should be
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedertransliterated to camlCase identifiers possibly adding a (consistent)
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedersuffix or prefix to avoid conflicts with keywords. However, instead of
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maedera recurring prefix or suffix you may consider to use qualified imports
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder=== Good Programming Practice ===
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."
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederMost haskell functions should be at most a few lines, only case
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederexpression over large data types (that should be avoided, too) may need
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maedercorresponding space.
1da2d3de72cd19f22820492bc832c9964762a64eChristian MaederThe code should be succinct (though not obfuscated), readable and easy to
1da2d3de72cd19f22820492bc832c9964762a64eChristian Maedermaintain (after unforeseeable changes). Don't exploit exotic language
1da2d3de72cd19f22820492bc832c9964762a64eChristian Maederfeatures without good reason.
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.)
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder case foo of Foo -> "Foo"
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder case <longer expression> of
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederAvoid the notation with braces and semicolons since the layout rule
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maederforces you to properly align your alternatives.
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.
1da2d3de72cd19f22820492bc832c9964762a64eChristian MaederDon't leave unused or commented-out code in your files! Readers don't
1da2d3de72cd19f22820492bc832c9964762a64eChristian Maederknow what to think of it.
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Case expressions ====
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederPrefer case expressions over pattern binding declarations with
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maedermultiple equations.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNot always nice:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder longFunctionName (Foo: _ : _) = e1
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder longFunctionName (Bar: _) = e2
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maeder longFunctionName arg = case arg of
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Foo : _ : _ -> e1
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder Bar : _ -> e2
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder _ -> error "ProgrammingGuidelines.longFunctionName"
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederhttp://research.microsoft.com/~simonpj/papers/haskell-retrospective/
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian Maederthe first example is said to be written in [[declaration style]]. The
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederequations look like written for a rewrite system (although their order
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maedermatters of course).
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederBut this declarative style is only nice for toy examples and annoying
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederif functions are renamed or if the number of arguments changes.
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian MaederThe other extreme (according to SPJ) is [[expression style]]:
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maeder longFunctionName = \ arg -> ...
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederWe don't propose this style either. We propose to use as much pattern
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maedermatching (including as-patterns) on a single left-hand-side as appropriate.
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederHowever, the expression style with a lambda term may come in handy, when
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian Maedersetting record fields of a function type.
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederWe avoid lambda expressions if this is easily possibly using the
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederPrelude functions const, flip, curry, uncurry or section notation or
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederplain partial application. We do not indroduce an auxiliary function only to
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederavoid the lambda, though.
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maeder==== Partial functions ====
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederFor partial functions do 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.
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederUsually a case statement (and the import of isJust and fromJust from
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian MaederData.[[Maybe]]) can be avoided by using the "maybe" function:
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder maybe (error "<ModuleName>.<function>") id $ Map.lookup key map
1da2d3de72cd19f22820492bc832c9964762a64eChristian MaederGenerally we require you to be more explicit about failure
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maedercases. Surely a missing (or an irrefutable) pattern would precisely
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederreport the position of a runtime error, but these are not so obvious
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederwhen reading the code.
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maeder==== Let or where expressions ====
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian MaederDo avoid mixing and nesting "let" and "where". (I prefer the
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederexpression-stylistic "let".) Use auxiliary top-level functions that
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maederyou do not export. Export lists also support the detection of unused
99882cd0f42bd3c1bff73170b174e14d1fde7dc4Christian Maeder==== Code reuse ====
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.
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Application notation ====
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.
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maeder f x : g x ++ h x
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maeder a == 1 && b == 1 || a == 0 && b == 0
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederRather than putting a large final argument in parentheses (with a
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maederdistant closing one) consider using "$" instead.
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maeder"f (g x)" becomes "f $ g x" and consecutive applications
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maeder"f (g (h x))" can be written as "f $ g $ h x" or "f . g $ h x".
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian MaederA function definition like
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder"f x = g $ h x" can be abbreviated to "f = g . h".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederNote that the final argument may even be an infix- or case expression:
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder map id $ c : l
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder filter (const True) . map id $ case l of ...
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederHowever, be aware that $-terms cannot be composed further in infix
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederProbably wrong:
31c6978fd9066c9d2c3c98c950f7abbe89112522Christian Maeder f $ x ++ g $ x
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederBut the scope of an expression is also limited by the layout rule, so
7e0943e4893be7d7d87059ef6995b59aa1d4e96bChristian Maederit is usually safe to use "$" on right hand sides.
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederOf course "$" can not be used in types. GHC has also some primitive
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederfunctions involving the kind "#" that cannot be applied using "$".
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian MaederLast warning: always leave spaces around "$" (and other mixfix
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maederoperators) since a clash with template haskell is possible.
bd8ff5b5f66be563e5be9d3a0c069e32d06f331cChristian Maeder(Also write "\ t" instead of "\t" in lambda expressions)
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== List Comprehensions ====
d71c501e69da3973406558162ad439da2b3464d6Christian MaederUse these only when "short and sweet". Prefer map, filter, and foldr!
d71c501e69da3973406558162ad439da2b3464d6Christian Maeder [toUpper c | c <- s]
d71c501e69da3973406558162ad439da2b3464d6Christian Maeder map toUpper s
d71c501e69da3973406558162ad439da2b3464d6Christian Maeder [toUpper c | s <- strings, c <- s]
d71c501e69da3973406558162ad439da2b3464d6Christian MaederHere it takes some time for the reader to find out which value depends
d71c501e69da3973406558162ad439da2b3464d6Christian Maederon what other value and it is not so clear how many times the interim
d71c501e69da3973406558162ad439da2b3464d6Christian Maedervalues s and c are used. In contrast to that the following can't be clearer:
d71c501e69da3973406558162ad439da2b3464d6Christian Maeder map toUpper (concat strings)
d71c501e69da3973406558162ad439da2b3464d6Christian MaederWhen using higher order functions you can switch easier to data
d71c501e69da3973406558162ad439da2b3464d6Christian Maederstructures different from list. Compare:
d71c501e69da3973406558162ad439da2b3464d6Christian Maeder map (1+) list
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Types ====
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 MaederUsing type synonyms consistently is difficult over a longer time,
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederbecause this is not checked by the compiler. (The types shown by
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederthe compiler may be unpredictable: i.e. FilePath, String or [Char])
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederTake care if your data type 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.
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian MaederBad (to handle arguments in sync):
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data Mode f p = Box f p | Diamond f p
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederGood (to handle arguments only once):
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder data BoxOrDiamond = Box | Diamond
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder data Mode f p = Mode BoxOrDiamond f p
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederConsider (bad):
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder data Tuple a b = Tuple a b | Undefined
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederversus (better):
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder data Tuple a b = Tuple a b
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder Maybe (Tuple a b)
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder(or another monad) whenever an undefined result needs to be propagated
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Records ====
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederFor (large) records avoid the use of the constructor directly and
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederremember that the order and number of fields may change.
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederTake care with (the rare case of) depend polymorphic fields:
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian Maeder data Fields a = VariantWithTwo
f09bc2a9244ed29b02ec8dd58a3040f8acb467baChristian Maeder { field1 :: a
f09bc2a9244ed29b02ec8dd58a3040f8acb467baChristian Maeder , field2 :: a }
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian MaederThe type of a value v can not be changed by only setting field1:
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder v { field1 = f }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederBetter construct a new value:
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder VariantWithTwo { field1 = f } -- leaving field2 undefined
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederOr use a polymorphic element that is instantiated by updating:
f09bc2a9244ed29b02ec8dd58a3040f8acb467baChristian Maeder empty = VariantWithTwo { field1 = [], field2 = [] }
f09bc2a9244ed29b02ec8dd58a3040f8acb467baChristian Maeder empty { field1 = [f] }
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.
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederHowever, I doubt if the following is a really good alternative to the
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederabove data Mode with data BoxOrDiamond.
1da2d3de72cd19f22820492bc832c9964762a64eChristian Maeder data Mode f p =
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder Box { formula :: f, positions :: p }
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder | Diamond { formula :: f, positions :: p }
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian MaederTry to strictly separate IO, Monad and pure (without do) function
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederprogramming (possibly via separate modules).
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder x <- return y
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 MaederThis will fail:
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder do s <- readFile f
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maeder writeFile f $ 'a' : s
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederbecause of lazy IO! (Writing is starting before reading is finished.)
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Trace ====
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.
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Imports ====
ce59e0cc5c7221245ed323290bfccbda4ee32dd9Christian MaederStandard library modules like Char. List, Maybe, Monad, etc should be
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederimported by their hierarchical module name, i.e. the base package (so
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederthat haddock finds them):
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian MaederThe libraries for Set and Map are to be imported qualified:
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder import qualified Data.Set as Set
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maeder import qualified Data.Map as Map
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder==== Glasgow extensions and Classes ====
4bb40320bdda9b90e305c1bc7982814ad7fcbc42Christian Maeder[[Use of language extensions|Stay away from 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
3db8b71dd1ccc662325b96a5ee8f351ace0293baChristian Maederclasses), functional dependencies (FD), undecidable and possibly incoherent
798a3d6fdcb8c17b0bc3502a150be75c9ec8799bChristian Maederinstances and then you are "in the wild" (according to SPJ).
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder=== Style in other languages ===
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder* [http://www.cs.caltech.edu/~cs20/a/style.html OCaml style]
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder=== Final remarks ===
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian MaederDespite guidelines, writing "correct code" (without formal proof
af1cb109bce240bcafe3823df022d6088cbfc438Christian Maedersupport yet) still remains the major challenge. As motivation to
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian Maederfollow these guidelines consider the points that are from the "C++
ed0055e8e720ca2d07e857e7852de91d47fab9e7Christian MaederCoding Standard", where I replaced "C++" with "Haskell".
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* programmers can go into any code and figure out what's going on
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* new people can get up to speed quickly
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* people new to Haskell are spared the need to develop a personal style and defend it to the death
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* people new to Haskell are spared making the same mistakes over and over again
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* people make fewer mistakes in consistent environments
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* programmers have a common enemy :-)
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* the standard is usually stupid because it was made by someone who doesn't understand Haskell
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* the standard is usually stupid because it's not what I do
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* standards reduce creativity
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* standards are unnecessary as long as people are consistent
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* standards enforce too much structure
46bf8457679e3dd601af7e35cc0966642ba09794Christian Maeder* people ignore standards anyway
51db8ddc5236cf8f39d87bf6139cbc9aad146828Christian Maeder[[Category:Style]]