我试图弄清楚 Haskell 是使用动态范围还是静态范围。我意识到,例如,如果您定义:
let x = 10
然后定义函数
let square x = x*x
您有 2 个不同的“x”,这是否意味着它是动态范围的?如果不是,它使用什么范围,为什么?
此外,Haskell 变量可以有别名(相同内存位置/值的不同名称)吗?
谢谢。
Haskell 使用(广义上)与大多数其他语言完全相同的词法作用域。
例如。
x = 10
导致在全局范围内引用的值x
,而
square x = x * x
将导致x
词法范围限定为函数 square。如果您认为上述形式是一种语法上的精巧,它可能会有所帮助:
square = \ x -> x * x
至于你的另一个问题,我不确定你所说的别名是什么意思
只回答问题的第二部分:
同一个“内存位置”可以有多个别名,但由于它们都是不可变的,所以大多数时候都没有关系。
愚蠢的例子:
foo x y = x * y
bar z = foo z z
在foo
调用 frombar
时,两者x
和y
显然是相同的值。但由于您不能修改x
或y
,您甚至不会注意到。
你的说法有些地方不对...
在您的示例中,函数中的x不是10只是square 的一个参数,它可以采用任何值(您可以稍后指定类型)在这种情况下为 10,但只是在这种情况下。
以下是Curt Sampson提供的别名示例:
import Data.IORef
main :: IO ()
main = do x <- newIORef 0 -- write 0 into x
readIORef x >>= print -- x contains 0
let y = x
readIORef y >>= print -- y contains 0
writeIORef x 42 -- write 42 into x
readIORef y >>= print -- y contains 42
由于问题的第一部分已经被其他人回答,这里是第二部分:
我假设aliasing
你的意思是one name for another
。由于 haskell 是一种函数式语言,并且函数在任何情况下都表现为普通标识符,因此您可以这样做:
y = x
y
这将为函数定义一个别名x
。请注意,一切都是函数。即使它看起来像一个“变量”,它也只是一个不带参数的空函数。类型的别名如下所示:
type Function = Double -> Double
Function
这将为该类型定义一个别名Double -> Double
在您的示例中,x 的全局定义被 x 的局部定义所掩盖。在 Haskell 中,变量的作用域由源代码的静态读取决定——这称为词法作用域,但可以得到类似于带有隐式参数的动态作用域的东西(但这可能导致一些意外行为(我读过;从不我自己试过))。
Haskell 使用静态嵌套作用域。与其他具有静态嵌套范围的语言相比,有点令人困惑的是,名称的范围是一个块,其中包括在其定义之前的测试。例如
evens = 0 : map (+1) odds
odds = map : (+1) evens
在这里,“赔率”这个名称在“偶数”的定义范围内,尽管令人惊讶的事实是“赔率”尚未定义。(该示例定义了两个无限的偶数和奇数列表。)
具有类似范围规则的死语言是 Modula-3。但是 Haskell 有点棘手,因为您可以尝试在同一范围内“重新定义”一个变量,但您只需引入另一个递归方程。对于首先学习 ML 或 Scheme 的人来说,这是一个陷阱:
let x = 2 * n
x = x + 1 -- watch out!
这是非常好的 ML 或 Scheme let*,但 Haskel 具有 scheme letrec 语义,不受 lambda 值的限制。难怪这是棘手的事情!
简明扼要地总结其他答案:
x = 1; y = x
但通常并不重要,因为事物是不可变的。let
您在示例中使用的语法看起来像是在交互式ghci>
提示中。交互模式中的所有内容都发生在 IO monad 中,因此那里的事情可能看起来比正常情况更易变。
好吧,正如我认为人们已经说过的那样,Haskell 没有在大多数其他语言中发现的任何变量,它只有表达式。在您的示例let x = 10
中,x 是一个始终计算为 10 的表达式。稍后您实际上无法更改 x 的值,尽管您可以使用范围规则通过将 x 定义为另一个表达式来隐藏它。
是的,Haskell 有别名。试试这个小程序:
import Data.IORef
main :: IO ()
main = do x <- newIORef 0 -- write 0 into x
readIORef x >>= print -- x contains 0
let y = x
readIORef y >>= print -- y contains 0
writeIORef x 42 -- write 42 into x
readIORef y >>= print -- y contains 42