同伦,它是如何工作的?

有人可以build议解释同伦性概念的文章,特别是使用Clojure。 为什么Clojure是同相的,但在Java等其他语言中很难做到这一点?

在我进行一些事情之前,我想补充一个答案,这里有一个更多的参考 – 与同形相关的部分相当短,但是Rich Hickey做了解释! Channel 9与Rich Hickey和Brian Beckman谈论了Clojure。 并发性是可以理解的主要关注点,但同相性确实得到了自己的(短暂的)屏幕时间,在此期间,Rich很好地解释了read之间的相互作用(将程序员logging的具体语法转换为内部表示从列表等)和eval 。 他有这个很好的图表显示eval如何从来不知道它所评估的代码是来自read操作的文本文件… Arthur已经解释了这个背后的要点,但嘿,无论如何看它,这是一个非常好的video!


免责声明:我将在下一个水平栏下面提到Java和Python作为示例。 我想清楚地说明下面的内容仅仅是为什么我认为可能很难做出一个homoiconic,Lisp风格的macrosJava或Python; 这只是一个学术练习,我不想考虑是否有任何理由尝试的问题。 另外, 我不想暗示具有Lisp样式macros的语言的语法必须包含用于树结构的明确分隔符 ; Dylan(不含义的Lisp?)显然提供了一个反例。 最后,我使用expressionLisp风格的macros,因为我只是在检查Lisp风格的macros。 例如,Forth语言有不同的macros观设施,除了我知道启用邪恶的酷代码之外,我不了解它。 显然,语法扩展可以通过多种方式来实现。 有了这个…


我想解决你的问题的第二部分 – 大多数编程语言是如何被认为不是同相的? 在这个过程中,我将不得不涉及Lisp的语义,但是由于Nils已经提供了关于术语“homoiconic”本身的良好信息源的链接,并且Arthur已经描述了read-> macro expand-> compile cycle在Clojure中,我将在下面继续讨论这个问题。 首先,引用Alan Kay的一段话(摘自维基百科的文章,也链接到原始资料):

交互式LISP和TRAC都是“同心圆的”,因为它们的内部和外部表示本质上是相同的。

(这些位隐藏了很多文本,但是这个要点没有改变。)

现在让我们问自己一个问题: 什么是Java的Java内部表示? 呃,这甚至没有意义。 Java 编译器确实具有Java的某种内部表示forms,即抽象语法树; 为了构造一个“homoiconic Java”,我们必须使这个AST表示成为Java中的第一类对象, 并且devise一个可以直接写AST的语法。 这可能certificate是相当困难的。

Python提供了一个非同母语言的例子,这个语言的有趣之处在于它目前以ast模块的forms提供了一个AST操作工具包。 该模块的文档明确指出Python AST可能会在不同版本之间变化,这可能会也可能不会令人沮丧; 不过,我想一个勤奋的程序员可以采用ast模块,devise一个用于直接描述Python AST的语法(也许基于Sexpression式,也许基于XML),并使用ast在常规Python中构build该语法的parsing器,第一步是用Python语义创build一个homoiconic语言。 (我相信我前段时间遇到了一个Lisp编译成Python字节码的方言……我想知道它是否可以在某种程度上做类似的事情?)

即使这样,问题仍然是从这种同心合性中获得具体的好处。 它被视为Lisp系列语言成员的有益属性,因为它允许我们编写编写进一步程序的程序,其中macros是最显着的。 现在, 尽pipe在Lisp中操作Lisp代码的内部表示很容易,但通过Lisp执行模型也可以启用一个同样重要的方法 :Lisp程序只是一个集合Lisp表单; 这些由Lisp函数eval处理,它负责确定expression式的值并在正确的时间引起适当的副作用; Lisp的语义正是eval的语义。 在合理快速的情况下,内部如何保持这种语义错觉的问题是一个实现细节; Lisp系统有义务向程序员公开一个函数eval ,并 Lisp程序正在被该函数处理一样。

在现代的Lisp系统中,它是eval合约的一部分,它执行一个额外的预处理阶段,在这个阶段macros在评估代码(或编译和运行,视情况而定)之前展开。 这个特定的工具并不是Lisp系统的必要组成部分,但是将它插入到这个执行模型中非常简单! 另外,我不知道这是不是使Lisptypes的macros观转换可pipe理的唯一执行模型,这意味着任何试图融入Lisptypesmacros的语言都不得不采用类似的执行模式。 我的直觉告诉我,事实确实如此。

当然,一旦语言直接并行地写成AST,并且使用类似于Lisp的执行模型和一个评估函数/对象,那么我们就不得不怀疑是否Lisp的另一种方言是即使AST并行语法恰好是基于XML的。 不寒而栗

当我学习Lisp时,当我得知Lisp是分两个阶段“编译”的时候,同心性的概念是有意义的,阅读和编​​译以及代码用这两个相同的数据结构表示:

  • 首先你想起了一个sexpression式
  • 然后在文件中键入sexpression式作为字符
  • 然后阅读器将文件中的字符转换为sexpression式。 它不编译程序,只是build立字符的数据结构,这是阅读阶段的一部分。
  • 然后读者查看每个expression式,并决定它们是否是一个macros,如果是的话运行macros来产生另一个sexpression式。 所以在这一点上,我们已经从sexpression式到字符到sexpression式,然后从sexpression式到不同的sexpression式。
  • 这些sexpression式然后被编译成可以由jvm运行的.class文件,这是“编译”阶段的第二个。

所以它从你的大脑到.class文件都是非常的sexpression式。 你甚至可以编写写sexpression式的sexpression式。 所以你可以说“代码是数据”或“代码是数据”,因为这听起来更好。

这是一个简短的程序来做符号分化。 这是LISP操纵自己的代码的一个例子。 尝试将其翻译成另一种语言,看看为什么LISP对这类事情有好处。

 ;; The simplest possible symbolic differentiator ;; Functions to create and unpack additions like (+ 1 2) (defn make-add [ ab ] (list '+ ab)) (defn addition? [x] (and (=(count x) 3) (= (first x) '+))) (defn add1 [x] (second x)) (defn add2 [x] (second (rest x))) ;; Similar for multiplications (* 1 2) (defn make-mul [ ab ] (list '* ab)) (defn multiplication? [x] (and (=(count x) 3) (= (first x) '*))) (defn mul1 [x] (second x)) (defn mul2 [x] (second (rest x))) ;; Differentiation. (defn deriv [exp var] (cond (number? exp) 0 ;; d/dx c -> 0 (symbol? exp) (if (= exp var) 1 0) ;; d/dx x -> 1, d/dx y -> 0 (addition? exp) (make-add (deriv (add1 exp) var) (deriv (add2 exp) var)) ;; d/dx a+b -> d/dx a + d/dx b (multiplication? exp) (make-add (make-mul (deriv (mul1 exp) var) (mul2 exp)) ;; d/dx a*b -> d/dx a * b + a * d/dx b (make-mul (mul1 exp) (deriv (mul2 exp) var))) :else :error)) ;;an example of use: create the function x -> x^3 + 2x^2 + 1 and its derivative (def poly '(+ (+ (* x (* xx)) (* 2 (* xx))) 1)) (defn poly->fnform [poly] (list 'fn '[x] poly)) (def polyfn (eval (poly->fnform poly))) (def dpolyfn (eval (poly->fnform (deriv poly 'x)))) 

“homoiconicity”的整个想法有点混乱,不适合Lisp。 Lisp中的内部和外部表示方式并不相同。 外部表示是基于文件中的字符。 内部表示是基于Lisp数据(数字,string,列表,数组等),并且是非文本的。 这与人物是如何相同的? 有内部表示,没有相应的外部表示(例如编译代码,闭包,…)。

Lisp和许多其他编程语言之间的主要区别是,Lisp对源代码有一个简单的数据表示 – 不是基于string的。

很明显,代码可以用基于文本的编程语言中的string表示。 但是在Lisp中,源代码可以用原始Lisp数据结构来表示。 外部表示是基于sexpression式,这是一个简单的模型来表示分层数据为文本。 内部模型是基于列表等的表示

这就是评估者得到的:内部表示。 不是1到1版本的文本input,但parsing。

基本模型:

  • READ将外部sexpression式转换为数据
  • EVAL以Lisp数据的forms获取Lisp表单并对其进行评估
  • PRINT将Lisp数据转换为外部sexpression式

请注意,READ和PRINT适用于任意Lisp数据,这些数据具有打印的表示forms和读取器,不仅适用于Lisp表单。 在Lisp编程语言中,表单按照定义是有效的expression式。

这似乎是显而易见的,但第一个来源可能是:

http://en.wikipedia.org/wiki/Homoiconicity

http://c2.com/cgi/wiki?DefinitionOfHomoiconic

一般来说,同伦性是可以解释的,你也可以find起源的来源。 正如使用Lisp的例子所解释的,它离Clojure不远。