简体   繁体   English

在Haskell中使用“ let”的功能纯度

[英]Functional Purity using 'let' in Haskell

As I am working on learning Haskell, I understand it is a purely functional language. 在学习Haskell的过程中,我了解它是一种纯粹的功能语言。 I am having trouble understanding why let -statements don't violate purity. 我很难理解为什么let陈述不会违反纯洁。

For example (in ghci): 例如(在ghci中):

Prelude> let e = exp 1
Prelude> e
2.718281828459045
Prelude> let e = 2
Prelude> e
2

isn't my second let statement producing a side effect? 我的第二个let语句不会产生副作用吗? Or is the second let statement a new closure? 还是第二个let语句重新闭合?

Your second let creates a new binding for e that shadows the existing variable. 第二个lete创建一个新的绑定,该绑定遮盖了现有变量。 It does not modify e . 它不会修改e You can easily check this with the following: 您可以使用以下方法轻松检查:

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> 

let introduces a new local variable with a single unalterable value, and it has more local scope than any surrounding definitions, so for example: let引入一个具有单个不可更改值的新局部变量,它的局部作用域比周围的任何定义都多,因此例如:

*Main> (let length = 2 in show length) ++ ' ':show (length "Hello")
"2 5"

Here the first length has the value 2, but its scope local to the brackets. 在这里,第一个length的值是2,但范围仅限于方括号。 Outside the brackets, length means what it has always meant. 在括号之外, length表示它一直以来的含义。 Nothing has been edited, just a more local variable has been introduced that happens to have the same name as another one in a different scope. 没有进行任何编辑,只引入了一个更多的局部变量,该变量恰好与另一个作​​用域的名称相同。 Let's make ghci mad by omitting the brackets and making it try to make length a number and a function: 让我们让ghci疯狂,省去括号并尝试使length成为数字和函数:

*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")

And here's your example: 这是您的示例:

*Main> let e = exp 1 in show e ++ " " ++ let e = 2 in show e
"2.718281828459045 2"

I'll add brackets to emphasise the scope: 我将添加方括号以强调范围:

*Main> let e = exp 1 in (show e ++ " " ++ (let e = 2 in (show e)))
"2.718281828459045 2"

The first e is hidden rather than edited. 第一个e被隐藏而不是被编辑。 Referential transparency is preserved, but it's definitely bad practice because it's hard to follow. 参照透明性得到保留,但是绝对不明智,因为它很难遵循。


Now secretly the interactive prompt is a bit like one big do block in the IO monad, so let's look at that: 现在,秘密地,交互式提示有点像IO monad中的一个大do块,所以让我们看一下:

testdo = do
  let e = exp 1
  print e
  let e = 2
  print e

Now I have to admit that looks an awful lot like breaking referential transparency, but bear in mind that this looks like it does too: 现在,我不得不承认,这看起来很像破坏参照透明性,但请记住,这看起来也确实如此:

testWrite = do
   writeFile "test.txt" "Hello Mum"
   xs <- readFile "test.txt"
   print xs
   writeFile "test.txt" "Yo all"
   xs <- readFile "test.txt"
   print xs

Now in what sense have we got referential transparency? 现在,在什么意义上我们已经获得了参照透明性? xs clearly refers to two different strings. xs显然是指两个不同的字符串。 Well, what does this do notation actually mean? 那么,这是什么do记号实际上意味着什么呢? It's syntactic sugar for 这是语法糖

testWrite = writeFile "test.txt" "Hello Mum"
         >> readFile "test.txt" 
         >>= (\xs -> print xs 
         >> writeFile "test.txt" "Yo all"
         >> readFile "test.txt"
         >>= (\xs -> print xs))

Now it's clearer that what looks like assignment is just local scope again. 现在,更清楚的是分配看起来又只是局部作用域。 You presumably are happy to do 您大概很高兴做

increment :: [Int] -> [Int]
increment = \x -> map (\x -> x+1) x

Which is doing the same thing. 哪个在做同样的事情。


Summary 摘要
What appeared to be assignment is just introduction of a new local scope. 似乎只是分配了一个新的本地范围。 Phew. ew If you use this a lot, you make it very unclear what your code means. 如果您经常使用此功能,则会很不清楚代码的含义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM