Search code examples
haskellscopewhere-clausedo-notation

Using variable from outer scope in where in do block


I'm trying to use the variable defined in the outer scope within an action defined in do block using where:

module Main where

main :: IO ()
main = do
    outerVar <- return "a"
    doSomething
    where
        doSomething :: IO String
        doSomething = putStrLn ("outerVar: " ++ outerVar)

Given this snippet, I'm trying to understand why does the compiler return the following error:

error: Variable not in scope: outerVar :: [Char]
  |
9 |         doSomething = putStrLn ("outerVar: " ++ outerVar)
  |

Based on my understanding - doSomething function should create some kind of a "closure" to contain the value of outerVar (which I've just found out is called a free variable), but that doesn't happen.

I've spent quite a lot of time trying to figure out why exactly does this error happen. I'm quite embarassed as, even for me as a Haskell newbie, it seems almost like a basic concept that should be obvious but it isn't - hopefully I'm wrong. I couldn't find any answers when searching for "where in do block scope", "where closure" or similar keywords. This page that compares let and where doesn't mention my case, the closest example I've found is the first bit of code from here where the "Lambda lifting" topic is covered. They're able to use n variable from outer scope within the function defined in where, but it's not in a do block as my case is.

So my question is - why exactly is outerVar variable not in scope of doSomething?


Solution

  • do blocks are just syntactic sugar for chaining monadic values using >>= and lambda functions. Your block is first translated by the compiler into:

    return "a" >>= \outerVar -> doSomething
      where doSomething =...
    

    It should be obvious now that outerVar is not in scope outside the lambda to which it is an argument.