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

sak*_*eth -2 variables haskell mutable immutability

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

重新分配变量

lef*_*out 8

首先,请注意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 ‘=’
Run Code Online (Sandbox Code Playgroud)

较新的版本通过简单地重写任何这样的表达式使这成为可能let x = 3,这一直是好的:

GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
Prelude> let x = 3
Prelude> x
3
Run Code Online (Sandbox Code Playgroud)

相比之下,在Haskell源文件中,let x = 3它本身从来就不合法.这仅适用于特定环境,即monadic do块.

main :: IO ()
main = do
   let x = 3
   print x
Run Code Online (Sandbox Code Playgroud)
3

并且设计中的GHCi提示实际上就像do块中的行一样,所以让我们在下面讨论它.请注意,我也可以写

main = do
   let x = 1
   let x = 3
   print x
Run Code Online (Sandbox Code Playgroud)
3

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

main =
   let x = 1
   in let x = 3
      in print x
Run Code Online (Sandbox Code Playgroud)

所以,你有两个嵌套的范围.当你在某个表达式中查找变量时,Haskell总是选择"最近的",即在内部范围中:

main =
   let x = 1
     ???
   in?let x = 3
     ?in print x
     ??
Run Code Online (Sandbox Code Playgroud)

外部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
  |        ^

Run Code Online (Sandbox Code Playgroud)

那里:第二个定义简单地引入了一个新的,更本地的变量,它也恰好被调用x,但与外部变量无关.即我们不妨重命名它们:

main = do
   let xOuter = 1
   let xInner = 3
   print xInner
Run Code Online (Sandbox Code Playgroud)

所有这一切的结果是以这种方式"变异"的变量对使用原始变量的其他函数没有影响.例:

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
Run Code Online (Sandbox Code Playgroud)

另一个结果是,尝试使用旧值的"更新"表现得很有趣:

Prelude> let s = "World"
Prelude> s = "Hello"++s
Prelude> s
"HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHell^C
Run Code Online (Sandbox Code Playgroud)

在这里,新的绑定也不会只是在前面加上"Hello"到老s="World".相反,它预先考虑"Hello"到其自身的结果值,这是通过定义反过来"Hello"添附到...等,递归.