Lisp中的同质性很容易看出:
(+ 1 2)
既是+
的函数调用,1
,2
作为参数,也是包含+
,1
和{的列表{1}}。它同时是代码和数据。
用朱莉娅这样的语言:
2
我知道我们可以将其解析为Julia中的1 + 2
:
Expr
然后我们可以获得AST并操纵它:
:(1 + 2)
因此,我们可以在Julia(和Elixir)中操纵程序的AST。但它们是否与Lisp一样具有同音性 - 是任何代码片段实际上只是语言本身的数据结构吗?
我不知道Julia中的julia> Meta.show_sexpr(:(1+2))
(:call, :+, 1, 2)
代码如何立即与数据类似,而Lisp中的1 + 2
只是一个列表。那么它仍然是杀人的吗?
答案 0 :(得分:42)
比尔克林顿的话说,“这取决于'是'的含义是什么”。嗯,好吧,不是真的,但它确实取决于“homoiconic”这个词的含义是什么。这个术语充满争议,我们不再说朱莉娅是同性恋的 - 所以你可以自己决定它是否合格。我将引用Kent Pitman(谁知道关于Lisp的一两件事)而不是试图定义同质性,而是在2001年的Slashdot interview中说:
我喜欢Lisp表达自己的意愿。人们经常将其解释为表现自己的能力,但我认为这是错误的。大多数语言都能够代表自己,但他们根本没有意愿。 Lisp程序由列表表示,程序员知道这一点。如果它是阵列就没关系。重要的是它的程序结构是代表的,而不是字符语法,但除此之外,选择是非常随意的。表示是Right®的选择并不重要。重要的是它是一个共同的,商定的选择,以便可以有一个丰富的程序操作程序社区,在这个共同的代表中“做交易”。
他也没有定义同音性 - 他可能不想进入定义论证而不是我。但他切入了问题的核心:一种语言能够表达自己的意愿吗? Lisp愿意走极端 - 你甚至无法避免它:作为数据的程序表示只是坐在那里,盯着你看。 Julia不使用S表达式语法,因此代码作为数据的表示不太明显,但它并没有隐藏得很深:
julia> ex = :(2a + b + 1)
:(2a + b + 1)
julia> dump(ex)
Expr
head: Symbol call
args: Array(Any,(4,))
1: Symbol +
2: Expr
head: Symbol call
args: Array(Any,(3,))
1: Symbol *
2: Int64 2
3: Symbol a
typ: Any
3: Symbol b
4: Int64 1
typ: Any
julia> Meta.show_sexpr(ex)
(:call, :+, (:call, :*, 2, :a), :b, 1)
julia> ex.args[3]
:b
julia> ex.args[3] = :(3b)
:(3b)
julia> ex
:(2a + 3b + 1)
Julia代码由Expr
类型(以及符号和原子)表示,虽然表面语法和结构之间的对应关系不那么明显,但它仍然存在。更重要的是,人们知道代码只是可以生成和操作的数据,因此有一个“丰富的程序操作程序社区”,就像KMP所说的那样。
这不仅仅是Julia代码作为数据结构的表面呈现 - 这就是Julia如何将代码表示给自己。在REPL中输入表达式时,它将被解析为Expr
个对象。然后将这些Expr
对象传递给eval
,将它们“降低”为更常规的Expr
个对象,然后将这些对象传递给类型推断,全部实现in Julia。关键是编译器使用与您看到的完全相同的代码表示。 Lisp的情况并没有那么不同。当你查看Lisp代码时,实际上并没有看到列表对象 - 那些只存在于计算机内存中的列表对象。你看到的是列表文字的文本表示,Lisp解释器解析并转化为列表对象,然后它就像朱莉娅一样。 Julia的语法可以看作是Expr
文字的文本表示 - Expr
恰好是一个比列表更不通用的数据结构。
我不知道细节,但我怀疑Elixir是相似的 - 也许José会说话。
另见: