wiki:Development/CodeConventions

Coding Conventions

The golden rule is to follow whatever conventions are already being used in the module you're editing. If you're creating a new module then follow the conventions used in existing modules.

If you're not sure what the conventions are, then ask. If you can think of a better way of doing it then let us know on the mailing list. This goes for development process and the use of library functions, as much as formatting and commenting style. The DDC project was started years ago, and times change...

Comments

  • Critital: If you tried to understand something but gave up before doing so, then open a ticket on the trac and set the ticket type to document. This is a bug against the maintainability of the software. Don't assume that the next person that looks at the same code will be able to make more sense of it than you could, or have more patience.
  • Each top-level definition should have a comment explaining what it is for. One liners are fine.
  • Running comments in the bodies of functions are encouraged. Write down what you were expecting the code to do when you wrote it, so it reads like a story. Aim for 1 comment line at least every 5-10 code lines, depending on how complex the code is. This level of commenting would be overkill for a cookie-cutter program like a database GUI, but compilers are a completely different beast. See the CheckExp module in the type checker for a good example.
  • All top-level bindings should have a type signature. Exceptions can be made for functions that are continuations of others, as they will never need to be called from outside the module they are defined in.
  • Feel free to add dividers like:
    -- Optimiser Monad -----------------------------------------------------------------
    

Such dividers increase the structure of the code and help the reader find things, especially with syntax highlighting editors. Most dividers should be 100, 80 or 40 chars wide.

Spacing

  • Tabs are 8 spaces. The reason being that when you cat a text file to a unix console they come out as 8 spaces. Keep your editor set to tabs-as-8-spaces or you'll end up committing misaligned code.
  • Corollary to the above: don't use tabs. Keep your editor set to 'soft-tabs' mode so that when you press the tab key it inserts 8 spaces.
  • Most code should fit in 80 columns, but it's ok to use up to 100 if needed. Don't go over 100 columns.

Layout

  • We try to avoid having multiple layout styles in DDC. Having different styles creates "accidental complexity", that is, syntactic differences in the source that don't equate to real functional differences. If we change layout style then this should be documented here, so that we can migrate the code base towards it.
  • If a function does several things in a regular way, then it should look like that in the source code. This means you should line up arguments to similar function calls. For example, use this:
     = do  someFunction      "Monday"    (23, 23)         (Just 'a')
           someOtherFunction "Tuesday"   ("this", "that") Nothing
           anotherFunction   "Wednesday" ('a', 'b') 
    

instead of this:

 = do  someFunction "Monday" (23, 23) (Just 'a')
       someOtherFunction "Tuesday" ("this", "that") Nothing
       anotherFunction "Wednesday" ('a', 'b') 
  • For the same reason, line up the '=' signs in groups of bindings. For example, use this:
      let  thing          = some stuff
           someOtherThing = some more stuff
           oneLastThing   = still more stuff
      in   ...
    

instead of this:

  let  thing = some stuff
       someOtherThing = some more stuff
       oneLastThing = still more stuff
  in   ...

Naming

  • Most names should use camel case style, but it's ok to use underscores in a postfix when naming different "versions" of a value. These look like subscripts for people used to LaTeX. For example:
     let  pastaSalad        = initialSalad
          pastaSalad_spiced = addSpice pastaSalad "Oregano"
          pastaSalad_baked  = bakeThing 100 pastaSalad_spiced
          ...
     in   ... pastaSalad_baked ...
    
    Avoid using names like pastaSalad' and pastaSalad'', because the prime doesn't provide any additional documentation.
  • Name conversion functions like globOfTops :: [Top] -> Glob instead of topsToGlob :: [Top] -> Glob. The type may be the opposite way around compared to the name, but it makes the source code easier to read. Consider (globOfTops someTops) vs (topsToGlob someTops).
  • If part of a variable name reflects its type, then put that part out the front. For example, source code variables of type Shared.Var should be named something like vThing, with a v out the front. Sets or lists of variables should be named vsThing with vs out the front. Avoid using names like thingVar and thingVars. We're not mandating Hungarian Notation, but if you're going to name a variable after a type anyway, then put the type part out the front.

Structure

  • Keep the sizes of the modules small. If a module has more than about 400 lines then consider breaking it up. Modules with just 100 lines are fine. Using small modules makes them easier to digest, forces you to think about the overall structure of the code, and helps parallel builds.

Module signatures

Like the this, with the commas on the left:

module Thing 
        ( someFunction
        , someOtherFunction
        , moreStuff )
where
...

Try to put the do on the same line as the =

Use this:

fun x 
 = do   y <- thing
        return (x + y)

Instead of this:

fun x = do
        y <- thing
        return (x + y)

The second version creates extra visual noise. As function names have different lengths, the column position of the top-level do will tend to be different between functions.

Put the $ on the left

Like this:

foo x
 = bar cheeze
 $ whatever baz
 $ thingo

Instead of this:

foo x
 = bar cheeze $ 
   whatever baz $ 
   thingo

This is also to reduce visual noise. Notice how in the first version all the symbols line up.

Prefer let over where

Prefer this:

niceFunction x 
 = let dude = thingo x
       epic = goodness dude
   in  baz epic dude

Instead of this:

niceFunction x = baz epic dude
 where dude = thingo x
       epic = goodness dude

Using a "where" is ok when baz does something trivial, like initialise a loop. However, any time "baz" does something interesting you should use a let, otherwise the conceptual flow of the code is ruined. Using let over where also makes it easier to convert to strict Disciple code.

Last modified 16 months ago Last modified on Dec 21, 2012 7:45:08 AM