Reputation: 10057
I've discovered that when doing IO in Haskell, variables that are assigned using the <-
operator are only in scope for the statements immediately after them - not in where
clauses.
For example:
main :: IO()
main = do
s <- getLine
putStr magic
where
magic = doMagic s
This won't work as s
is not in scope. I did some research to confirm it and found this article:
Unlike a
let
expression where variables are scoped over all definitions, the variables defined by<-
are only in scope in the following statements.
So how can I make s
available for use in the where
clause?
Upvotes: 4
Views: 1332
Reputation: 668
let
is the practical way to handle this. But it may be worth unpacking what's really going on here. The do
statement isn't what's causing the scoping trouble.
Remember that
main = do
s <- getLine
putStr magic
where
magic = doMagic s
is equivalent to this:
main = getline >>= \s ->
putStr magic
where magic = doMagic s
If we put in some parentheses, this is:
main = getline >>= (\s -> putStr magic) where magic = doMagic s
This is where the scope of s
comes from: it's the argument to a lambda expression, and it only exists within that lambda expression. Try to use it in a where
clause outside of that lambda, and you get a "not in scope" error.
let
works because let
and where
parse differently. With parentheses for clarity:
foo = (\x -> let y = x in y) -- Works fine
foo'' = (\x -> y) where y = x -- y is not in scope
This is what's causing the problem; it's not specific to IO, or to do
statements.
Upvotes: 1
Reputation: 47052
In addition to the general let form, there's a special let form for use with do syntax you can use instead:
main :: IO()
main = do
s <- getLine
let magic = doMagic s
putStr magic
magic
is available in all following lines in the block.
Upvotes: 6
Reputation: 11976
Well, magic
is a function. Therefore you can do something like:
magic m = doMagic m
Or:
magic = \m-> doMagic m
And call it like so:
putStrLn $ magic s
Of course, as you already researched, the sensible thing to do when you can reuse your computed magic
is to use a let ... in
expression, and nest the calls:
let magic_str = magic s in
putStrLn magic_str
Upvotes: 1