Python's is more restrictive: you must skip a line before starting a multi-line indented block; i.e., this is not legal:
if a == b: print "asdf"
print "a"
else:
print "a"
This lets you decide on a very simple rule for dealing with whitespace indentation: Each new block is started by inserting a carriage return and the proper number of tabs.Not so in Haskell, because you are given the freedom to put the first line of a block in the same line as the block starter:
let x = 1
y = 2
do x <- 1
y <- 2
Now you need to decide whether to be 3 or 4 spaces in depending on whether it is a let or do block. You cannot use the rule, because the appropriate number of spaces is no longer a multiple of your indentation unit.That's just the simple case, because you are also allowed to put these block starters (let, do, case, etc) at arbitrary points in an expression:
f = let x = 1 in let y = x + 2 in
y + 1
This is syntactically correct Haskell; but personally I like the idea of lexical scope being represented by indentation level, which is not reflected here.It becomes very easy to get yourself into situations where you cannot use the Python rule. But you can also impose restrictions on yourself so you _can_ use that rule. Like always treating let, do, in, etc as "{"'s in C:
f = let
x = 1
in
let
y = x + 2
in
y + 1
This may look a little verbose. But in real cases there would be a lot more statements there. In the middle of all this I want to maintain the idea "number of tabs corresponds to lexical scope". We can also push the analogy to "{"'s in C and adopt the "K&R" style, but on block-starting expressions: f = let
x = 1 in let
y = x + 2 in
y + 1
There's also the solution of editors that just figure out where to indent, in which case we can make it look pretty and still get the indentation right. I think it's best to develop a consistent style that will work across editors though.