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
-
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. ↩
-
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. ↩
-
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. ↩