我听说 Haskell 变量是不可变的,但我能够重新分配和更新变量值 [duplicate]

I heard that Haskell variables are immutable but i am able to reassign and update variable values [duplicate]

提问人:saketh 提问时间:4/17/2018 最后编辑:willeM_ Van Onsemsaketh 更新时间:4/17/2018 访问量:392

问:

我听说 Haskell 变量是不可变的,但我能够重新分配和更新变量值

Reassigning variables

变量 哈斯克尔 不变性 可变

评论

2赞 willeM_ Van Onsem 4/17/2018
不,你不是:你在更严格的范围内引入了一个同名的新变量。这是不同的东西,因为你可以(好吧,不是直接在ghci中,因为在这里你基本上是在线构造一个单子),构造一个逃脱这个范围的单子,然后它将把外在变量作为值。
0赞 saketh 4/17/2018
好的,谢谢Willem Van Onsem
1赞 duplode 4/17/2018
请不要将文本(例如控制台输出)作为图像发布 - 只需直接复制并粘贴文本即可。这对每个人来说都更容易。

答:

1赞 Jordan Mackie 4/17/2018 #1

你是在影子,而不是变异。

评论

4赞 willeM_ Van Onsem 4/17/2018
虽然严格来说这是正确的,但我认为你应该扩展这个答案,并给出更严格的解释这里发生的事情。
8赞 leftaroundabout 4/17/2018 #2

首先,请注意,GHCi 语法与 Haskell 源文件语法并不完全相同。特别是,实际上曾经是非法的:x = 3

GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
Prelude> x = 3

<interactive>:2:3: parse error on input ‘=’

较新的版本通过简单地将任何此类表达式重写为 ,这一直是可以的:let x = 3

GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
Prelude> let x = 3
Prelude> x
3

相比之下,在 Haskell 源文件中,它本身从来都不是合法的。这仅适用于特定环境,即单子 dolet x = 3

main :: IO ()
main = do
   let x = 3
   print x
3

GHCi 提示在设计上实际上就像块中的线条一样工作,所以让我们在下面讨论一下。请注意,我也可以写do

main = do
   let x = 1
   let x = 3
   print x
3

这基本上就是你的GHCi会议中发生的事情。然而,正如其他人所说,这不是突变,而是阴影。要了解其工作原理,请注意,以上内容本质上是一种速记的写作方式

main =
   let x = 1
   in let x = 3
      in print x

因此,您有两个嵌套范围。当你在某个表达式中查找一个变量时,Haskell 总是选择“最接近的变量”,即在内部作用域中:

main =
   let x = 1
     ┌──
   in│let x = 3
     │in print x
     └─

外部根本没有被触及,它基本上与内部范围内发生的任何事情都无关。如果询问您的文件中是否有任何可疑内容,编译器实际上会警告您:x

$ ghc -Wall wtmpf-file16485.hs 
[1 of 1] Compiling Main             ( wtmpf-file16485.hs, wtmpf-file16485.o )

wtmpf-file16485.hs:3:8: warning: [-Wunused-local-binds]
    Defined but not used: ‘x’
  |
3 |    let x = 1
  |        ^

wtmpf-file16485.hs:3:12: warning: [-Wtype-defaults]
    • Defaulting the following constraint to type ‘Integer’
        Num p0 arising from the literal ‘3’
    • In the expression: 3
      In an equation for ‘x’: x = 3
      In the expression:
        do let x = 1
           let x = 3
           print x
  |
3 |    let x = 1
  |            ^

wtmpf-file16485.hs:4:8: warning: [-Wname-shadowing]
    This binding for ‘x’ shadows the existing binding
      bound at wtmpf-file16485.hs:3:8
  |
4 |    let x = 3
  |        ^

那里:第二个定义只是引入了一个新的、更局部的变量,它恰好也被调用,但与外部变量无关。也就是说,我们不妨重命名它们:x

main = do
   let xOuter = 1
   let xInner = 3
   print xInner

所有这一切的结果是,以这种方式“突变”的变量对使用原始变量的其他函数没有影响。例:

GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/sagemuej/.ghci
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Prelude> let x = 1
Prelude> let info = putStrLn ("x is ="++show x++" right now")
Prelude> x = 3
Prelude> info
x is =1 right now

另一个结果是,尝试使用旧值的“更新”以一种有趣的方式运行:

Prelude> let s = "World"
Prelude> s = "Hello"++s
Prelude> s
"HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHell^C

在这里,新绑定不仅在旧绑定之前附加。相反,它以自己的结果值为前置,而结果值又由预置到...依此类推,递归。"Hello"s="World""Hello""Hello"