同像性

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

在计算机编程中,同像性(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). 

外部連結[编辑]