Search code examples
haskellscopefunctional-programmingmonadsdo-notation

Do statement under a where clause


I'm trying to convert IO [String] to [String] with <- binding; however, I need to use a do block to do that under a where statement, but Haskell complains about the indentation all the time. Here is the code:

decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words
 | words' /= [] = block : (decompEventBlocks . drop $ (length block) words')
 | otherwise = []
  where 
   do
    words' <- words
    let block = (takeWhile (/="END") words')

What is the reason for that ? And how can we use do block in a where statement ? Moreover, is there any chance that we can have some statements before the guards ?


Solution

  • Remember: do-blocks are syntactic sugar for monadic notation. This means the following applies:

    do {a; b} = a >> b
    dp {a <- b; c} = b >>= \a -> c
    

    In other words, when using do-notation, you are actually producing values. This is why you can't just have a do-block in the top level of your where statement.

    The way to solve this is to put the function into a do-block:

    decompEventBlocks :: IO [String] -> IO [[String]]
    decompEventBlocks words = do
        -- We unwrap the IO [String], but we keep it in the do-block,
        -- because it must be kept in a monadic context!
        words' <- words 
        let block = (takeWhile (/="END") words')
        -- This is equivalent to the guards you had in your function.
        -- NB return :: Monad m => a -> m a, to keep it in a monadic context!
        if not $ null words'
            then do 
              -- Since the recursion is monadic, we must bind it too:
              rest <- decompEventBlocks $ return $ drop (length block) words'
              return $ block : rest
            else return []
    

    To learn about monads, do-notation, >>=, and >>, I highly reccommend reading the LYAH chapters to gain a good understanding before attempting more monadic code.