Skip to content

Code Blocks and Indentation

If you have programmed in Python before, then you are used to indentation with meaning. Code blocks that would be delimited by curly brackets in languages such as C, C++, Rust or Java are defined by their indentation level in Python. The same is true in Haskell.1

Indentation is used to delimit code blocks and statements that span multiple lines.

The precise rules are a bit tedious to spell out here (and I don't know them formally myself). I simply give examples that illustrate the most important rules and comment on them. Most of these examples will not make much sense to you yet because you don't know yet what these expressions mean, but it's a bit of a chicken-and-egg situation. Do I get the basic syntax rules, such as indentation rules, out of the way before talking about the types of control flow constructs Haskell offers or do I talk about the control flow constructs and explain the indentation rules on the fly? I opted for the former.

The following are valid versions of if-then-else expressions:

if y > 2 then x + 1 else x + 2

if y > 2 then x + 1
         else x + 2

if y > 2 then
    x + 1
else
    x + 2

The rules here are that the else needs to be indented at least as far as the if. If you choose to put the contents of the then-branch and of the else-branch onto new lines, then they each need to be indented at least one more character than the preceding line.2

In a case expression, the equivalent of switch in C, only more powerful, the different branches need to be indented the same and at least one more character than the case keyword:

case x of
    0 -> 2
    1 -> 3
    y -> 2 * y

The contents of class and instance declarations are blocks and need to be indented at least one more character than the class or instance keyword:

class Functor f where
    fmap :: (a -> b) -> (f a -> f b)

instance Functor Maybe where
    fmap _ Nothing  = Nothing
    fmap f (Just x) = Just (f x)

The same goes for declarations that follow where or let and expressions that follow in. They all need to be indented at least one character more than the keyword that precedes them:

veclen :: (Double, Double) -> Double
veclen (x, y) = sqrt (x2 + y2)
  where
    x2 = x * x
    y2 = y * y

or

veclen :: (Double, Double) -> Double
veclen (x, y) =
    let x2 = x * x
        y2 = y * y
    in  sqrt (x2 + y2)

The statements in a do block need to be indented at least one more than the do keyword, and they need to be indented the same. We'll talk about do blocks a lot later in this book.

Finally, we have rules concerning continuation lines. In C or C++, indentation is completely arbitrary because statements are ended with semicolons. In Python, a statement ends at the end of a line unless we explicitly indicate that it continues on the next line, by putting a backslash at the end of the current line.3 In Haskell, the continuation of the current statement on the next line is once again indicated by indentation. If the next line is a continuation of the current line, it needs to be indented at least one character more than the current line:

add x y =
   x + y

add x y
   = x + y

add x y = x
   + y

  1. Haskell does have curly brackets and does allow you to write multiple statements on the same line, separated by semicolons. You may (very rarely) see it used in some code examples. Don't use this feature. It is rarely used and makes your code less readable. I've never used it in over 10 years of programming in Haskell. 

  2. The compiler is happy with one extra character of indentation. That's all it needs to recognize the body of the then or else-branch. For a human, one character of indentation is not visually distinctive enough. So, use at least two characters of indentation. I tend to use 4. 

  3. There are exceptions to this rule. If the current line would be incomplete as a statement—for example, because we have not closed an opening parenthesis yet—then the Python interpreter assumes that the statement continues on the next line even without the backslash.