lisp - Elixir 和 Julia 之类的语言在什么意义上是同音的?

标签 lisp julia homoiconicity

Lisp 中的同质性很容易看出:
(+ 1 2)
是对 + 的函数调用与 1 , 2作为参数,以及包含 + 的列表, 1 , 和 2 .它同时是代码和数据。

但是,在像 Julia 这样的语言中:
1 + 2
我知道我们可以将其解析为 Expr在 Julia :
:(1 + 2)
然后我们可以获取 AST 并对其进行操作:
julia> Meta.show_sexpr(:(1+2)) (:call, :+, 1, 2)
因此,我们可以在 Julia(和 Elixir)中操作程序的 AST。但是它们是否与 Lisp 具有相同的意义——任何代码片段真的只是语言本身的数据结构吗?

我看不出像 1 + 2 这样的代码在 Julia 中,立即像数据一样 (+ 1 2)在 Lisp 中只是一个列表。那它还是谐音吗?

最佳答案

用比尔克林顿的话来说,“这取决于‘是’这个词的含义是什么”。好吧,不是真的,但这确实取决于“homoiconic”一词的含义。这个词引起了足够的争议,我们不再说 Julia 是 homoiconic——所以你可以自己决定它是否符合条件。与其试图定义同质性,我将引用 Kent Pitman (谁知道一两件关于 Lisp 的事情)在 Slashdot interview 中说回到 2001 年:

I like Lisp's willingness to represent itself. People often explain this as its ability to represent itself, but I think that's wrong. Most languages are capable of representing themselves, but they simply don't have the will to. Lisp programs are represented by lists and programmers are aware of that. It wouldn't matter if it had been arrays. It does matter that it's program structure that is represented, and not character syntax, but beyond that the choice is pretty arbitrary. It's not important that the representation be the Right® choice. It's just important that it be a common, agreed-upon choice so that there can be a rich community of program-manipulating programs that "do trade" in this common representation.



他也没有定义同质性——他可能和我一样不想进入一个定义性的争论。但他切入了问题的核心:一种语言有多愿意代表自己? 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 一样。 Julia 的语法可以看作 Expr 的文本表示。文字 – Expr恰好是一个比列表更不通用的数据结构。

我不知道细节,但我怀疑 Elixir 是相似的——也许 José 会插话。

更新(2019)

在过去 4 年多的时间里,我更多地考虑了这一点,我认为 Lisp 和 Julia 之间的主要区别在于:
  • 在 Lisp 中,代码的语法与用于表示该代码的数据结构的语法相同。
  • 在 Julia 中,代码的语法与表示该代码的数据结构的语法完全不同。

  • 为什么这很重要?在支持 Julia 的一方,人们喜欢事物的特殊语法,并且经常发现 S-expression 语法不方便或令人不快。在亲 Lisp 方面,当您尝试生成(表示代码)的数据结构的语法与您通常编写的代码的语法相同时,弄清楚如何正确进行元编程要容易得多.这就是为什么当人们尝试在 Julia 中编写宏时,最好的建议之一是执行以下操作:
  • 编写一个您希望宏生成的代码类型的示例
  • 调用 Meta.@dump在该代码上将其视为数据结构
  • 编写代码来生成该数据结构——这是您的宏。

  • 在 Lisp 中,您不必执行第 2 步,因为代码的语法已经与数据结构的语法相同。有 quasiquoting(在 Lisp 中)quote ... end:(...) Julia 中的构造,它允许您使用代码语法构造数据结构,但这仍然不如让它们首先使用相同的语法那么直接。

    另见:
  • https://docs.julialang.org/en/v1/manual/metaprogramming/
  • What is a "symbol" in Julia?
  • 关于lisp - Elixir 和 Julia 之类的语言在什么意义上是同音的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31733766/

    相关文章:

    lisp - 如何使用不对称(未配对)的任意括号创建宏

    sockets - 使用clip下载网站

    functional-programming - mapcar 的多个参数

    Julia 多次调度与子类型不匹配

    lisp - 同调性水平

    clojure - 如何使 clojure 程序结构更容易识别?

    emacs - Emacs Lisp 中的 foldr、foldl 等价物是什么?

    struct - 为什么 NamedTuples 和(不可变的)结构是分开的?

    vectorization - 对特定参数的函数进行向量化