同像性

维基百科,自由的百科全书
跳到导航 跳到搜索

在计算机编程中,同像性(homoiconicity来自希腊语单词,homo-意为相同,icon含义表像),是某些编程语言的特殊属性,它意味着一个程序的结构与其句法是相似的,因此易于通过阅读程序来推测程序的内在涵义。如果一门编程语言具备了同像性,说明该语言的文本表示(通常指源代码),与其抽象语法树(AST)具有相同的结构(即AST和语法是同形的)。该特性允许使用相同的表示语法,将语言中的所有代码当成数据,来存取以及转换,提供了“代码即数据”的理论前提。

简介[编辑]

同像性编程语言中,程序的主要呈现方式,也是语言本身原始类型中的资料结构。这使得元编程更加容易,因为程序代码可以被视为资料:语言中的反射(运行时检查程序的实体)取决于单一的、性质相同的结构,而且它不必去处理,其它一些不同结构所导致的复杂语法。换句话说,同像性是程序的源代码即是基本的资料结构,而这个语言本身知道如何存取源码的文本。

Lisp编程语言,是具有同像性质的典型范例,它的设计很容易进行对列表的操作,而且其语法结构,即采用嵌套列表形式的S-表达式[1]。LISP程式以列表的形式来编写,所以可在运行时存取本身拥有的函数和程序,并以编程的方式重新设计自己。具有同像属性的语言,通常有对句法巨集的全面支持,允许程序员以简明的方式来表达程序的变换。这类语言有Clojure(现代流行的LISP方言),RebolRefal英语Refal,以及最近的Julia等等编程语言。

历史[编辑]

同像性一词的原始来源,是论文《编译器语言的巨集指令扩展》[2]。根据早期具影响力的论文《TRAC英语TRAC (programming language)文本处理语言》中提到[3]

主要设计目标之一,是TRAC的输入脚本(用户所输入的),应该相同一致于指示TRAC处理器内部动作的文本。换句话说,TRAC程序应该是以字串被储存于记忆体中,正如同用户在键盘上键入它们一样。如果TRAC程序本身发展成为新的程序,同一个脚本中也应该陈述列出这些新程序。TRAC处理器在其操作中,将此脚本直译为其程序。换句话说,TRAC解析器(处理器),将计算机有成效地转换为,具有新程序语言(TRAC语言)的新计算机。程序或过程资讯,在任何时候的呈现,都应该相同于TRAC处理器执行期间,对其作用的形式。我们期望内部代码的字符表示,和外部代码表示,相同一致或非常相似。在本TRAC实作中,内部字符基于ASCII,因为TRAC程序和文本,在处理器内部和外部,都具有相同的表示,所以术语同像性(homoiconic)一词是适用的,homo涵义相同,icon义为呈现。

[...]

跟从沃伦·麦卡洛克的提议,依据查尔斯·桑德斯·皮尔士的术语,参见道格拉斯·麦克罗伊的“编译器语言的巨集指令扩展”,ACM通讯,页214-220; 1960年4月。

艾伦·凯在他1969年的博士论文中,使用并可能推广了同像性这个术语[4]

所有先前的系统中,显著的一组例外是Interactive LISP[...]和TRAC。两者都是函数导向的(一为列表,另一为字符串),都用一种语言与用户交谈,并且都具有 “同像性的”,因为它们内部和外部表示本质上相同。它们都具有动态创建新函数的能力,然后可随著用户的兴趣进阶发展。他们唯一最大的缺点是,以它们写出的程序看起来就像,苏美尔人把布尔那·布里亚什国王的信写成巴比伦楔形文![...]

用途及优缺点[编辑]

同像性的一个优点是,以新概念扩展语言通常变得更简单,因为表示代码的资料,可在程序的元和基本层之间传递。函数的抽象语法树,可以作为元层中的资料结构来组成和操作,然后被评估。它可以更容易理解如何操作代码,因为它可以被理解为简单的资料(语言本身的格式亦同为资料格式)。

允许这样做的简单性也带来了一个缺点:有人认为至少在类似LISP的列表导向的语言的情况下,它会消除许多能帮助人们分析语言结构的视觉线索,而可能导致陡峭的学习曲线[5]

同像性的典型演示是元循环求值(meta-circular evaluator,同于REPL)。

实作方法[编辑]

所有范纽曼型架构的系统,其中包括绝大多数当今的通用计算机,由于原始机器代码在记忆体中执行的资料类型是位元组,可以隐含地描述为具有同像性。但是这个功能也可以在编程语言层别就抽取出来。

Lisp及其方言例如SchemeClojureRacket等,使用S-表达式来实现同像性。

其他被认为具有同像性的语言包括:

同像性语言的编程范例[编辑]

Lisp[编辑]

Lisp使用S-表达式作为资料和源码的外部表示。可以用基本函数READ读取S-表达式。READ回传Lisp资料:列表、符号、数字和字串。基本函数EVAL使用以资料形式呈现的Lisp源码,计算副作用并得出返回结果。结果由基本函数PRINT打印出来,从Lisp资料产生一个外部的S-表达式。

Lisp资料是含有不同类型的列表:(子)列表,符号,字串和整数。

((:name "john" :age 20) (:name "mary" :age 18) (:name "alice" :age 22))

以下Common Lisp源码范例使用列表,符号和数字。

(* (sin 1.1) (cos 2.03))      ; 中綴表示法為 sin(1.1)*cos(2.03)

使用基本函数LIST产生上面的表达式,并将变量EXPRESSION设置为结果:

(defvar expression)
-> EXPRESSION

(setf expression  (list '* (list 'sin 1.1) (list 'cos 2.03)) )  
-> (* (SIN 1.1) (COS 2.03))    ; Lisp傳回並打印結果

(third expression)    ; 表達式中的第三項
-> (COS 2.03)

COS这项变更为SIN

(setf (first (third expression)) 'SIN)
; 變更之後的表達式為 (* (SIN 1.1) (SIN 2.03)).

评估表达式:

(eval expression)
-> 0.7988834

将表达式打印到字串:

(princ-to-string expression)
->  "(* (SIN 1.1) (SIN 2.03))"

从字串中读取表达式:

(read-from-string "(* (SIN 1.1) (SIN 2.03))")
->  (* (SIN 1.1) (SIN 2.03))     ; 傳回一個其中有列表,數字和符號的列表

Prolog[编辑]

Prolog是同像性语言并且提供了很多反射设施。

1 ?- X is 2*5.
X = 10.

2 ?- L = (X is 2*5), write_canonical(L).
is(_, *(2, 5))
L = (X is 2*5).

3 ?- L = (ten(X):-(X is 2*5)), write_canonical(L).
:-(ten(A), is(A, *(2, 5)))
L = (ten(X):-X is 2*5).

4 ?- L = (ten(X):-(X is 2*5)), assert(L).
L = (ten(X):-X is 2*5).

5 ?- ten(X).
X = 10.

6 ?-

在第4行建立一个新子句。算符:-分隔一个子句的头部和主体。通过assert/1将它增加到现存的子句中,即增加它到“数据库”,这样我们可以以后调用它。在其他语言中可以称为“在运行时间建立一个函数”。还可以使用abolish/1retract/1从数据库中移除子句。注意在子句名字后的数,是它可以接受的实际参数的数目,它也叫做元数

我们可以查询数据库来得到一个子句的主体:

7 ?- clause(ten(X),Y).
Y = (X is 2*5).

8 ?- clause(ten(X),Y), Y = (X is Z).
Y = (X is 2*5),
Z = 2*5.

9 ?- clause(ten(X),Y), call(Y).
X = 10,
Y = (10 is 2*5).

call类似于Lisp的eval函数。

Rebol[编辑]

Rebol可巧妙的演示将代码当作数据来操纵和求值的概念。Rebol不像Lisp,不要求用原括号来分隔表达式。下面是Rebol代码的例子,注意>>表示解释器提示符,出于可读性而在某些元素之间增加了空格:

>> repeat i 3 [ print [ i "hello" ] ]
1 hello
2 hello
3 hello

在Rebol中repeat事实上是内建函数而非语言构造或关键字。通过将代码包围在方括号中,解释器不求值它,而是将它当作包含字的块:

[ repeat i 3 [ print [ i "hello" ] ] ]

这个块有类型block!,并且使用近乎赋值的语法,可以进一步的将它指定为一个字的值,这种语法实际上可以被解释器理解为特殊类型set-word!,并采用一个字跟随一个冒号的形式:

>> block1: [ repeat i 3 [ print [ i "hello" ] ] ] ;; 将这个块的值赋值给字`block1`
== [repeat i 3 [print [i "hello"]]]
>> type? block1 ;; 求值字`block1`的类型
== block!

这个块仍可以使用Rebol中提供的do函数来解释,它类似于Lisp中的eval。有可能审查块的元素并变更它们的值,从而改变要求值代码的行为:

>> block1/3 ;; 这个块的第三个元素
== 3
>> block1/3: 5 ;; 设置第三个元素的值为5
== 5
>> probe block1 ;; 展示变更了的块
== [repeat i 5 [print [i "hello"]]]
>> do block1 ;; 求值这个块
1 hello
2 hello
3 hello
4 hello
5 hello

另见[编辑]

参考文献[编辑]

引用
  1. ^ Wheeler, David A. Readable Lisp S-expressions. [2022-01-29]. (原始内容存档于2022-01-29). 
  2. ^ McIlroy, Douglas. Macro Instruction Extensions of Compiler Languages. Comm. ACM. 1960, 3 (4): 214–220. doi:10.1145/367177.367223. 
  3. ^ Mooers, C.N.; Deutsch, L.P. TRAC, A Text-Handling Language. Proceeding ACM '65 Proceedings of the 1965 20th national conference. 1965: 229–246. doi:10.1145/800197.806048. 
  4. ^ Kay, Alan. The Reactive Engine (学位论文). University of Utah. 1969 [2014-03-28]. (原始内容存档于2018-09-15). 
  5. ^ The Lisp Curse. [2022-01-29]. (原始内容存档于2022-05-16). 
  6. ^ 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 Homoiconic Languages. [2020-04-23]. (原始内容存档于2013-04-23). 
  7. ^ Lispy Elixir. 8thlight.com. [2022-01-29]. (原始内容存档于2022-03-05). Elixir, on the surface, is not homoiconic. However, the syntax on the surface is just a facade for the homoiconic structure underneath. 
  8. ^ Why we created Julia. julialang.org. [2020-04-23]. (原始内容存档于2019-02-19). We want a language that’s homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. 
  9. ^ metaprogramming. docs.julialang.org. [2020-04-23]. (原始内容存档于2013-05-04). Like Lisp, Julia represents its own code as a data structure of the language itself. 
  10. ^ Shapiro, Ehud Y.; Sterling, Leon. The art of Prolog: advanced programming techniques. MIT Press. 1994. ISBN 0-262-19338-8. 
  11. ^ R Language Definition (PDF): 6, 2021-05-18 [2022-01-29], (原始内容 (PDF)存档于2022-04-22), ... the semantics are of the FPL (functional programming language) variety with stronger affinities with Lisp and APL. In particular, it allows “computing on the language”, which in turn makes it possible to write functions that take expressions as input, something that is often useful for statistical modeling and graphics. 
  12. ^ "expression: Unevaluated Expressions", [2022-01-29], (原始内容存档于2022-01-29) 
  13. ^ Homoiconic languages (archived), in true Blue blog at Oracle
  14. ^ Ramsay, S.; Pytlik-Zillig, B. Code-Generation Techniques for XML Collections Interoperability. dh2012 Digital Humanities Conference Proceedings. 2012 [2020-04-23]. (原始内容存档于2016-03-03). 
  15. ^ Metaprogramming in mathematica. Stack Exchange. [2020-04-23]. (原始内容存档于2018-08-20). Mathematica is [...] Homoiconic language (programs written in own data structures - Mathematica expressions. This is code-as-data paradigm, like Lisp which uses lists for this) 
  16. ^ Notes for Programming Language Experts. Wolfram Language. Wolfram. 2017 [2020-04-23]. (原始内容存档于2022-01-04). 

外部链接[编辑]