[Haskell-cafe] Code layout in Emacs' haskell-mode

Jules Bean jules at jellybean.co.uk
Tue May 15 05:19:21 EDT 2007


Christopher L Conway wrote:
> I've installed 2.3 and it exhibits the same indentation behavior: any
> entity appearing on a new line immediately after "module X where"
> wants to be indented 4 spaces, including function definitions and
> variable bindings. 

Yes, it does do that. And it's correct syntax too. But I tend to 
override it by putting the first line flush-left. After that, it knows.


> "if-then-else" want to be lined up with one
> another, although both GHC and Hugs reject this layout.
>

I'll leave that issue for now, it's fiddly. I thought it was fixed in 
recent GHCs, but it's not something haskell-mode has ever got right.

> Here's the result of indent-region on the prior example:

No! No!

You can't use indent-region on haskell code. Why not? Because 
indentation involves *semantic* choices! And how can haskell-mode 
possibly guess the semantics of your code?

indent-region just chooses the 'first choice' in the fairly arbitrary 
list of choices for each line, I think. You could argue that it could 
have better heuristics, and guess better more often. Alternatively (and 
perhaps this makes more sense) you could argue that indent-region should 
never change the semantics; rather it should just normalise the 
indentation. I.e. it should be merely a pretty-printer.

>
> module Num where
>
>    import IO
>
>        main = do
>             putStrLn "Enter a number: "
>                      inp <- getLine
>                             let n = read inp
>                                     if n == 0
>                                     then putStrLn "Zero"
>                                     else putStrLn "NotZero"
>
> This is distressing, because I've gotten rather used to letting Emacs
> worry about indentation for me. (This works well in tuareg-mode for
> OCaml. But, as I said earlier, I am a layout-sensitive-language
> newbie.)

You need to get used to hitting <tab> until you see the indentation you 
want. There are choices to make and emacs can't make them (all) for you.

For example, consider the fragment after the 'let'.

What you have written is equivalent to this:

let n = read inp (if n == 0 then putStrLn "Zero" "NotZero")

I.e. you are treating read as a two-argument function, and the second 
argument is the entire if expression.



Haskell layout is really quite simple, when you get used to it. The 
problem is it's not that easy to explain to someone from an imperative 
background; because you have to cross two bridges at once - the 
'imperative' bridge and the 'layout' bridge.

Layout isn't active 'everywhere' in haskell. In fact, arguably, it isn't 
even active in 'most' haskell. The two contexts in which layout mode 
applies most often are do expressions and declarations. Declarations is 
'mostly' the top level of the file (it also applies if you define more 
than one name in a let or a where, but that's a relatively advanced 
technique). Do expressions are introduced by 'do' as you can see.

I urge you to experiment more in the REPL (ghci/hugs) and less with 
loading files, and find lots of examples on the web, and hopefully this 
will all become clear.

Jules


More information about the Haskell-Cafe mailing list