Haskell indentation

Derek Elkins ddarius@hotpop.com
Tue, 26 Aug 2003 21:35:23 -0400


On Tue, 26 Aug 2003 11:58:20 -0700
John Meacham <john@repetae.net> wrote:

> On Tue, Aug 26, 2003 at 02:00:32PM +0200, Per Larsson wrote:
> > I have problems finding a pleasing indentation style for haskell
> > code. Especially nested do-blocks have a tendency to run away to the
> > right margin. When looking on source code from experienced haskell
> > programmers, there seems not to be any consensus at all, everyone
> > uses their own convention and in many cases one changes style in the
> > same module.
> > 
> > Also, the automatic tools are problematic: the emacs mode I'm using
> > bails out in certain contexts and there are few user customizations
> > available. The haskell-src module in the GHC library offers a parser
> > and pretty-printer for haskell code with nice options for
> > customizing the indentation, but it can't handle comments which is a
> > problem if you want to use it as a basis for implementing a
> > indentation tool.
> > 
> > Is there anyone who have given this some thought and have some
> > suggestions for a consistent indentation style and/or desktop tools
> > which I'm not aware of?
> 
> I highly recommend the always-enter model. which means when you are
> using layout you always do a linebreak after any block forming
> construct(do, let, while, ...) and indent one more softtab level than
> the surrounding code. this has a number of advantages:
>  * no need for special modes or editor support
>  * indents are always an integral number of softtabs.
>  * code doesn't run off the right side of the screen since your
>    indentation level is relative to the start of the line, not the
>    expression that started it.
>  * cut-n-paste of code blocks is easier.
> 
> examples of what I mean can be seen here:
> http://repetae.net/john/computer/haskell/
> 
> I have known several people to get turned off of haskell when trying
> to recreate the indent style usually found in publications by hand... 
> 
>         John 

That style is pretty much what I use.  Looking at the "examples" (well,
one, Format.hs) though, there are a few things I do differently and
typically see done differently.

The first is that I always put the where for local function definitions
on a new line, e.g.
foo x = x + a + b
    where a = 1 + x
          b = 2 * x
I find this a lot clearer and prettier than,
foo x = x + a + b where
    a = 1 + x
    b = 2 * x
The only awkward thing I can think of is rearranging the local
definitions; the first case needs special handling.  However, I don't
typically do that.  Also to be clear, I still write,
class Foo a where
    foo :: a -> Bool

Another thing I commonly see and use for data types with many alternates
is the following style,

data AST
  = Const Int
  | Var String
  | Lam String AST
  | App AST AST
  | Let String AST AST
  | Add AST AST

Some other tricky instances are large pattern matches, in which case
I put the = on the next line indented, e.g. 
foo (Let v1 e1 (Let v2 e2 (Lam x body)))
    = ...

With my particular style if you want where blocks after a do block then
the indentation won't be right if you go to a tabstop.  Personally, I
don't care about exactly lining my code up on tabstops, so I use two
spaces (four spaces being a tab for me) in those cases, e.g.
foo = do
    a <- bar
    b <- baz
    return (a+b)
  where bar = return 1
        baz = return 2

Another difficult case that I haven't solidified a style for is large
type signatures.

Finally, you can use higher-order functions to handle some cases pretty
cleanly.  For example, I use 'either' quite a bit.