我发现在Haskell中执行IO时,使用<-
运算符分配的变量仅在紧跟在它们之后的语句的范围内 - 而不是在where
子句中。
例如:
main :: IO()
main = do
s <- getLine
putStr magic
where
magic = doMagic s
这不起作用,因为s
不在范围内。我做了一些研究来确认它并找到this文章:
与变量在所有定义范围内的
let
表达式不同,<-
定义的变量仅在以下语句的范围内。
那么如何才能在s
子句中使用where
?
答案 0 :(得分:6)
除了一般的let表单外,还有一个特殊的let表单,可以使用do语法来代替:
main :: IO()
main = do
s <- getLine
let magic = doMagic s
putStr magic
magic
在块中的所有后续行中都可用。
答案 1 :(得分:1)
嗯,magic
是一个函数。因此,您可以执行以下操作:
magic m = doMagic m
或者:
magic = \m-> doMagic m
并称之为:
putStrLn $ magic s
当然,正如您已经研究的那样,当您可以重用计算的magic
时,要做的明智之处是使用let ... in
表达式并嵌套调用:
let magic_str = magic s in
putStrLn magic_str
答案 2 :(得分:1)
let
是解决这个问题的实用方法。但是可能值得解开这里真正发生的事情。 do
语句不是造成范围问题的原因。
请记住
main = do
s <- getLine
putStr magic
where
magic = doMagic s
相当于:
main = getline >>= \s ->
putStr magic
where magic = doMagic s
如果我们加上一些括号,那就是:
main = getline >>= (\s -> putStr magic) where magic = doMagic s
这是s
的范围来自:它是lambda表达式的参数,它只存在于该lambda表达式中。尝试在该lambda之外的where
子句中使用它,并且会出现“not in scope”错误。
let
有效,因为let
和where
的解析方式不同。括号内容为清晰起见:
foo = (\x -> let y = x in y) -- Works fine
foo'' = (\x -> y) where y = x -- y is not in scope
这就是导致问题的原因;它不是特定于IO或do
语句。