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

Haskell中的不可变变量是什么意思?,haskell,functional-programming,immutability,ghci,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 这是否与不可变变量的概念相冲突 非常感谢 Haskell不允许修改现有变量。然而,它确实允许您

我对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
这是否与不可变变量的概念相冲突


非常感谢

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
f
无需关心您是否定义了一个新变量,该变量也称为
a
;从它的角度来看,
a
是一个始终保持不变的变量


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

a = 1
a = 2

main :: IO ()
main = print a
GHCi中允许这样做的原因是它与Haskell模块的工作方式不同。GHCi命令序列实际上形成了IO monad†中的一系列动作;i、 e.该计划必须

main :: IO ()
main = do
   let a = 1
   let a = 2
   print a
现在,如果你已经了解了单子,你就会知道这只是语法糖

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块中不允许的某些操作,特别是您可以定义
数据
类型和
es。但是任何正常的语句或变量定义都与
IO
monad中的一样。

(使用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中“绑定”,不能“变异”。换句话说,上面的程序相当于(称为α-等效,仅表示绑定变量名称的一致更改;有关更多详细信息,请参阅)


显然
x1
x2
是不同的变量。

这在我的rple中不起作用…试着把
a=1;a=2
.hs
文件中,并使用
ghc
编译它。GHCi命令是IO操作,因此就像键入的所有内容都在
do
块中一样。这意味着对相同名称的后续赋值就像嵌套的
s对旧绑定进行阴影处理。明白了!非常感谢。这让我非常困惑。另外:尝试GHCi(即使在单独的行中)
let a=4;设fx=x+a;设a=0;f 2
并查看
6
作为答案,确认旧
a
的值确实没有更改。缺少一点:GHCi允许
数据
新类型
类型
实例
声明,Haskell
do
语法中不允许使用。鉴于GHCi是一个交互式环境,它是一个基本功能。有多少次定义输入错误?如果每次这样做时都必须选择一个全新的名称,那么很容易就会出现无法读取的名称,如
myFunction7
,其中以前所有
myFunction
的定义都包含错误,并且在使用正确的定义时必须记住始终添加该
7
,对。如果这是必需的,那么我们就需要调整一个完全不同的工作流,可能是有明确范围的。我认为,GHCi的顶层实际上更像ML而不是Haskell。
main =
  let x = 1 in
  let f y = x + y in
  let x = 2 in
  print (x * f 3)
main =
  let x1 = 1 in
  let f y = x1 + y in
  let x2 = 2 in
  print (x2 + f 3)