Haskell中的不可变变量是什么意思?

时间:2016-06-26 16:45:58

标签: haskell functional-programming immutability ghci

我对Haskell中不可变变量的概念感到困惑。似乎我们无法改变Haskell中变量的值。但是当我尝试在GHCI中使用代码时,似乎变量的值确实发生了变化:

Prelude> foo x=x+1
Prelude> a=1
Prelude> a
1
Prelude> foo a
2
Prelude> a=2
Prelude> a
2
Prelude> foo a
3

这与不可变变量的想法有冲突吗?

非常感谢!

2 个答案:

答案 0 :(得分:19)

Haskell不允许您修改现有变量。但是,它确实允许你重用变量名,这就是这里发生的一切。一种看待这种情况的方法是使用:i[nfo]指令询问GHCi,其中声明了变量:

Prelude> let a = 1
Prelude> :i a
a :: Num a => a     -- Defined at <interactive>:2:5
Prelude> let a = 2
Prelude> :i a
a :: Num a => a     -- Defined at <interactive>:4:5

这些实际上是两个完全独立的,不同的变量,恰好被称为同名!如果你只是要求a,那么新的定义将是“首选”,但旧的定义仍然存在 - 如评论中chi所述,一种方法可以使用a在一个函数中:

Prelude> let a = 2
Prelude> :i a
a :: Num a => a     -- Defined at <interactive>:4:5
Prelude> let f x = a + x
Prelude> let a = 3
Prelude> f (-2)
0

f永远不需要关心您定义了一个名为a的新变量;从它的角度来看a是一个永远保持原样的不可变变量。

值得一谈的是为什么GHCi更喜欢后面的定义。这样做否则会在Haskell代码中发生;特别是如果您尝试编译以下模块,它只会给出有关重复定义的错误:

a = 1
a = 2

main :: IO ()
main = print a

在GHCi中允许这样的事情的原因是它与Haskell模块的工作方式不同。 GHCi命令的顺序实际上形成了IO monad中的动作序列 ;即该计划必须是

main :: IO ()
main = do
   let a = 1
   let a = 2
   print a

现在,如果你已经了解了monads,你就会知道这只是语法糖

main =
   let a = 1 in (let a = 2 in (print a))

这对于为什么你可以重复使用名称a来说真的是至关重要的一点:第二个,a = 2,生活的范围比第一个更窄 。所以它更本地化,本地定义具有优先权。这是一个好主意是否有点值得商榷;一个很好的论据就是你可以拥有像

这样的功能
greet :: String -> IO ()
greet name = putStrLn $ "Hello, "++name++"!"

并且它不会因为有人在其他地方定义

而停止工作
name :: Car -> String
name car | rollsOverAtRightTurn car   = "Reliant Robin"
         | fuelConsumption car > 50*litrePer100km
                                      = "Hummer"
         | ...                        = ...

此外,在GHCi中愚弄你可以“重新定义”变量真的非常有用,即使它这样一个好主意,在适当的程序中重新定义东西,这应该显示一致的行为。

正如dfeuer所说,这不是全部真相。你可以在GHCi中做一些IO do-block中不允许的事情,特别是你可以定义data类型和class es。但任何正常的陈述或变量定义都与IO monad中的行为一样。

答案 1 :(得分:0)

(使用GHCi的另一个答案很好,但澄清它不是GHCi或monad特有的......)

从以下Haskell程序

的事实可以看出
main =
  let x = 1 in
  let f y = x + y in
  let x = 2 in
  print (x * f 3)

打印8而不是10,变量只能在Haskell中“绑定”而不是“变异”。换句话说,上面的程序是等价的(称为α等效,意味着只对绑定变量名称进行一致更改;有关详细信息,请参阅https://wiki.haskell.org/Alpha_conversion

main =
  let x1 = 1 in
  let f y = x1 + y in
  let x2 = 2 in
  print (x2 + f 3)

很明显,x1x2是不同的变量。