aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/_posts/2018-02-18-peculiar-indentation.md
blob: 9edebf9c0a99db1437245dd6c378cbc891c9484b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
---
title: Peculiar Haskell indentation
excerpt: >
  An explanation for nasty `parse error`s I used to get for nested `do`
  blocks.
category: Haskell
---
I've fallen into a Haskell indentation pitfall.
I think it must be common, so I'm describing it here.

The problem is that indentation rules in `do` blocks are not intuitive to me.
For example, the following function is valid Haskell syntax:

```haskell
foo1 :: IO ()
foo1 =
    alloca $ \a ->
    alloca $ \b ->
    alloca $ \c -> do
        poke a (1 :: Int)
        poke b (1 :: Int)
        poke c (1 :: Int)
        return ()
```

In fact, this funnier version is also OK:

```haskell
foo2 :: IO ()
foo2 = alloca $ \a ->
     alloca $ \b ->
   alloca $ \c -> do
       poke a (1 :: Int)
       poke b (1 :: Int)
       poke c (1 :: Int)
       return ()
```

If you add an outer `do` however, things become a little more complicated.
For example, this is the valid version of the functions above with an outer
`do`:

```haskell
foo3 :: IO ()
foo3 = do
    alloca $ \a ->
        alloca $ \b ->
            alloca $ \c -> do
                poke a (1 :: Int)
                poke b (1 :: Int)
                poke c (1 :: Int)
                return ()
```

Notice the extra indentation for each of the `alloca`s.
When I tried to remove these seemingly excessive indents, GHC complained with
the usual `parse error (possibly incorrect indentation or mismatched
brackets)`.

```haskell
foo4 :: IO ()
foo4 = do
    alloca $ \a ->
    alloca $ \b ->
    alloca $ \c -> do
        poke a (1 :: Int)
        poke b (1 :: Int)
        poke c (1 :: Int)
        return ()
```

The truth is, the rules for desugaring `do` blocks are surprisingly simple and
literal.
GHC inserts semicolons according to the rules [found in the Wikibook].
So it inserts semicolons between the `alloca`s on the same level, so `foo4`
becomes:

```haskell
foo4 :: IO ()
foo4 = do
    { alloca $ \a ->
    ; alloca $ \b ->
    ; alloca $ \c -> do
        { poke a (1 :: Int)
        ; poke b (1 :: Int)
        ; poke c (1 :: Int)
        ; return ()
        }
    }
```

[found in the Wikibook]: https://en.wikibooks.org/wiki/Haskell/Indentation#Explicit_characters_in_place_of_indentation

The semicolons after `->` are clearly invalid Haskell syntax, hence the error.

P.S. To compile the functions above, you need to include them in a module and
add proper imports, e.g.

```haskell
module PeculiarIndentation where

import Foreign.Marshal.Alloc (alloca)
import Foreign.Storable      (poke)
```