diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2018-02-18 04:33:41 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2018-02-18 04:33:41 +0300 |
commit | af8f79e5a720f83f61e99fc9bd03e1c624d785b7 (patch) | |
tree | b5fa31b8708d556a78ccb61ac66f9c4024aadaa0 /_drafts/peculiar_indentation.md | |
parent | enable post categories (diff) | |
download | blog-af8f79e5a720f83f61e99fc9bd03e1c624d785b7.tar.gz blog-af8f79e5a720f83f61e99fc9bd03e1c624d785b7.zip |
import "Peculiar Haskell indentation" form a gist
Diffstat (limited to '')
-rw-r--r-- | _drafts/peculiar_indentation.md | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/_drafts/peculiar_indentation.md b/_drafts/peculiar_indentation.md new file mode 100644 index 0000000..e8378e1 --- /dev/null +++ b/_drafts/peculiar_indentation.md @@ -0,0 +1,106 @@ +--- +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 +--- +A Haskell indentation pitfall I've fallen into. +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. +First, GHC inserts semicolons according to the rules described here: +https://en.wikibooks.org/wiki/Haskell/Indentation#Explicit_characters_in_place_of_indentation. +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 () + } + } +``` + +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) +``` |