clojure - 为什么 Clojure 区分符号和变量?

标签 clojure lisp symbols

我看到了this question已经,但这并不能解释我的疑惑。

当我刚从Common Lisp 来到Clojure 时,我很疑惑为什么它把符号和关键字作为单独的类型来对待,但后来我想通了,现在我觉得这是一个绝妙的想法。现在我想弄清楚为什么符号和变量是不同的对象。

据我所知,Common Lisp 实现通常使用结构表示“符号”,该结构具有 1) 名称字符串,2) 在函数调用位置求值时指向符号值的指针,3) 指向外部调用位置评估时的值,以及 4) 属性列表等。

忽略 Lisp-1/Lisp-2 的区别,事实仍然是在 CL 中,“符号”对象直接指向它的值。换句话说,CL 将 Clojure 所称的“symbol”和“var”组合在一个对象中。

在 Clojure 中,要计算一个符号,首先必须查找相应的 var,然后必须取消引用 var。为什么 Clojure 以这种方式工作?这样的设计可能有什么好处?我知道 var 具有某些特殊属性(它们可以是私有(private)的、常量的或动态的...),但这些属性不能简单地应用于符号本身吗?

最佳答案

其他问题涉及符号的许多真实方面,但我会尝试从另一个角度解释它。

符号就是名字

与大多数编程语言不同,Clojure 区分事物 和事物的名称。在大多数语言中,如果我说类似 var x = 1 的内容,那么说“x 是 1”或“x 的值是 1”是正确且完整的。但在 Clojure 中,如果我说 (def x 1),我做了事:我创建了一个 Var(一个值持有实体),并且我用符号 x 为它命名。说“x 的值为 1”并不能完全说明 Clojure 中的全部情况。更准确(虽然麻烦)的说法是“由符号 x 命名的 var 的值为 1”。

Symbols 本身只是名称,而 vars 是承载值的实体,本身没有名称。如果扩展前面的示例并说 (def y x),我还没有创建新的 var,我只是给现有的 var 起了第二个名字。 xy 这两个符号都是同一个 var 的名称,其值为 1。

打个比方:我的名字是“卢克”,但这与我本人不同,与我这个人不同。这只是一个词。在某些时候我可以更改我的名字并非不可能,而且还有很多其他人与我同名。但在我的 friend 圈中(如果你愿意,在我的命名空间中),“卢克”这个词意味着我。在幻想的 Clojure 世界中,我可以是一个为你携带值(value)的 var。

但为什么呢?

那么为什么这个额外的名称概念与变量不同,而不是像大多数语言那样将两者混为一谈?

一方面,并​​非所有符号都绑定(bind)到变量。在局部上下文中,例如函数参数或 let 绑定(bind),代码中符号引用的值实际上根本不是 var - 它只是一个局部绑定(bind),当它遇到编译器。

不过,最重要的是,它是 Clojure 的“代码就是数据”理念的一部分。这行代码 (def x 1) 不仅仅是一个表达式,它还是数据,特别是一个由值 defx1。这非常重要,尤其是对于将代码作为数据进行操作的宏而言。

但是如果 (def x 1) 是一个列表,那么列表中的值是多少?特别是,这些值的类型是什么?显然 1 是一个数字。但是 defx 呢?当我将它们作为数据进行操作时,它们的类型是什么?答案当然是符号。

这就是符号在 Clojure 中是一个独特实体的主要原因。在某些上下文中,例如宏,您想要获取名称并对其进行操作,脱离运行时或编译器授予的任何特定含义或绑定(bind)。名称必须是某种东西,而它们就是符号。

关于clojure - 为什么 Clojure 区分符号和变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11662084/

相关文章:

math - ( Racket /计划)减法产生的结果非常小

java - 找不到符号错误,java似乎认为对象/方法不存在

java - 在另一种方法中使用 getter 不起作用?

java - 在 Windows 10 上使用 Clojure/Java 的 NginX - 未启动

macros - 宏扩展到相同的运算符

clojure - 这个 `doseq`语句和 `for`语句有什么区别;在 Clojure 中读取文件?

sorting - 从 lisp 中的输入流中读取多项式

ruby-on-rails - 定义一个 :class_name with a string or a symbol in Rails 3

groovy - 您对Groovy有何看法?

java - 实现 Clojure 库