aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/_posts/2018-02-18-peculiar-indentation.md
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2018-02-18 04:46:38 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2018-02-18 04:46:38 +0300
commit3b6f0d4c7d3bd2a53469c5b0484908f42d9c066f (patch)
tree7d297266afe862507658de32eb9d9df67f403d17 /_posts/2018-02-18-peculiar-indentation.md
parentpeculiar: make it a proper post (diff)
downloadblog-3b6f0d4c7d3bd2a53469c5b0484908f42d9c066f.tar.gz
blog-3b6f0d4c7d3bd2a53469c5b0484908f42d9c066f.zip
peculiar: move to _posts/
Diffstat (limited to '_posts/2018-02-18-peculiar-indentation.md')
-rw-r--r--_posts/2018-02-18-peculiar-indentation.md107
1 files changed, 107 insertions, 0 deletions
diff --git a/_posts/2018-02-18-peculiar-indentation.md b/_posts/2018-02-18-peculiar-indentation.md
new file mode 100644
index 0000000..9c6930d
--- /dev/null
+++ b/_posts/2018-02-18-peculiar-indentation.md
@@ -0,0 +1,107 @@
+---
+title: Peculiar Haskell indentation
+layout: post
+excerpt: >
+ An explanation of how Haskell indentation works inside <code>do</code>
+ blocks.
+category: Haskell
+custom_css:
+ - syntax.css
+---
+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)
+```