Haskell list monad and return ()

Viewed 136

Consider the following piece of code:

foo :: [Int]
foo = do
  x <- [1..10]
  if x < 5 then pure () else [] -- Control.Monad.guard (x < 5)
  pure x

foo2 :: [Int]
foo2 = 
  [1..10] >>= \x ->
    if x < 5 then pure x else [] >>= \y ->
      pure y

In foo, I have manually inlined Control.Monad.guard (x < 5), as noted in the comment.

Why does foo compile, even though there is pure () in the code? How does the [()] pass the type check? Is it a special case for the do syntax? If yes, is it described anywhere?

In foo2, I attempted to "desugar" foo without the do syntax. Note that there can't be any pure (), as it doesn't pass type checking.

I'm using ghc-8.8.4 if that's important.

1 Answers

You have several errors in your manual desugaring. One attempt that uses only >>= would be:

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >>= \_ ->
      pure x

First, the parentheses are important: you're binding the result of the entire if expression, not performing a bind inside of its else branch. Second, you can't just introduce a new variable y and then use it in your pure result. Desugaring preserves the same expressions in your source code, it just moves them around a bit. So, pure x must desugar to pure x.

Hopefully you can see why this works: the type of () does not matter, because nobody ever looks at its value, and the result, pure x, has the right type regardless.

But really GHC doesn't produce quite this code: x >>= \_ -> y is equivalent to x >> y, and that's what's used for a statement in a do-block that does not bind its result to a variable. So really you get

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >> pure x

And you can, if you prefer, use a slightly fancier operator from Functor to achieve the same result. Let's un-inline guard, and use (<$) :: Functor f => a -> f b -> f a instead of a monadic operation. x <$ y is the same as y >> pure x:

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    x <$ guard (x < 5)
Related