当我正在学习Haskell时,我理解它是一种纯函数式语言。我无法理解为什么let
- 陈述不违反纯度。
例如(在ghci中):
Prelude> let e = exp 1
Prelude> e
2.718281828459045
Prelude> let e = 2
Prelude> e
2
不是我的第二个let
声明会产生副作用吗?或者第二个let
语句是一个新的闭包?
答案 0 :(得分:21)
您的第二个let
为e
创建了一个隐藏现有变量的新绑定。它不会修改e
。您可以使用以下方法轻松检查:
Prelude> let e = 1
Prelude> let f () = "e is now " ++ show e
Prelude> f ()
"e is now 1"
Prelude> let e = 2
Prelude> e
2
Prelude> f ()
"e is now 1"
Prelude>
答案 1 :(得分:16)
let
引入了一个具有单个不可更改值的新局部变量,并且它具有比任何周围定义更多的局部范围,例如:
*Main> (let length = 2 in show length) ++ ' ':show (length "Hello")
"2 5"
这里第一个length
的值为2,但其范围是括号的局部范围。在括号之外,length
表示它一直意味着什么。没有编辑任何内容,只引入了一个更多的局部变量,该变量恰好与另一个不同范围的变量具有相同的名称。让我们通过省略括号并使其尝试使length
成为数字和函数来使ghci疯狂:
*Main> let length = 2 in show length ++ ' ':show (length "Hello")
<interactive>:1:14:
No instance for (Num ([Char] -> a0))
arising from the literal `2'
Possible fix: add an instance declaration for (Num ([Char] -> a0))
In the expression: 2
In an equation for `length': length = 2
In the expression:
let length = 2 in show length ++ ' ' : show (length "Hello")
<interactive>:1:19:
No instance for (Show ([Char] -> a0))
arising from a use of `show'
Possible fix: add an instance declaration for (Show ([Char] -> a0))
In the first argument of `(++)', namely `show length'
In the expression: show length ++ ' ' : show (length "Hello")
In the expression:
let length = 2 in show length ++ ' ' : show (length "Hello")
这是你的榜样:
*Main> let e = exp 1 in show e ++ " " ++ let e = 2 in show e
"2.718281828459045 2"
我将添加括号以强调范围:
*Main> let e = exp 1 in (show e ++ " " ++ (let e = 2 in (show e)))
"2.718281828459045 2"
隐藏第一个e
而不是编辑。参考透明度得以保留,但这绝对是不好的做法,因为它很难遵循。
现在秘密地说,交互式提示有点像IO monad中的一个大do
块,所以让我们看一下:
testdo = do
let e = exp 1
print e
let e = 2
print e
现在我不得不承认,看起来非常像破坏参照透明度,但请记住,这看起来也是如此:
testWrite = do
writeFile "test.txt" "Hello Mum"
xs <- readFile "test.txt"
print xs
writeFile "test.txt" "Yo all"
xs <- readFile "test.txt"
print xs
现在我们在什么意义上得到了参考透明度? xs
明确指出两个不同的字符串。那么,do
符号实际上意味着什么呢?
testWrite = writeFile "test.txt" "Hello Mum"
>> readFile "test.txt"
>>= (\xs -> print xs
>> writeFile "test.txt" "Yo all"
>> readFile "test.txt"
>>= (\xs -> print xs))
现在更清楚的是,看起来像赋值的只是局部范围。你可能很乐意做到
increment :: [Int] -> [Int]
increment = \x -> map (\x -> x+1) x
这是做同样的事情。
<强>摘要强>
看似分配的只是引入新的本地范围。唷。如果你经常使用它,你会很清楚你的代码意味着什么。