Lisp如何让您重新定义语言本身?

Lisp如何让您重新定义语言本身?,lisp,Lisp,我听说Lisp可以让你重新定义语言本身,我也尝试过对它进行研究,但没有明确的解释。有人举一个简单的例子吗?我要说的是,在定义新语法时,这个方案不同于普通的Lisp。它允许您使用define syntax定义模板,无论在哪里使用,这些模板都会应用到源代码中。它们看起来就像函数,只是在编译时运行并转换AST 下面是一个示例,说明如何根据lambda定义let。带有let的行是要匹配的模式,带有lambda的行是生成的代码模板 (define-syntax let (syntax-rules ()

我听说Lisp可以让你重新定义语言本身,我也尝试过对它进行研究,但没有明确的解释。有人举一个简单的例子吗?

我要说的是,在定义新语法时,这个方案不同于普通的Lisp。它允许您使用
define syntax
定义模板,无论在哪里使用,这些模板都会应用到源代码中。它们看起来就像函数,只是在编译时运行并转换AST

下面是一个示例,说明如何根据
lambda
定义
let
。带有
let
的行是要匹配的模式,带有
lambda
的行是生成的代码模板

(define-syntax let
  (syntax-rules ()
    [(let ([var expr] ...) body1 body2 ...)
     ((lambda (var ...) body1 body2 ...) expr ...)]))

请注意,这与文本替换完全不同。实际上,您可以重新定义
lambda
,而上面对
let
的定义仍然有效,因为它在定义
let
的环境中使用了
lambda
的定义。基本上,它像宏一样强大,但像函数一样干净。

宏是这样说的常见原因。因为代码只是一个数据结构(树,或多或少),所以可以编写程序来生成这个数据结构。因此,关于编写生成和操作数据结构的程序,您所知道的一切都增加了您表达代码的能力

宏并不是对语言的完全重新定义,至少就我所知(我实际上是一个阴谋家;我可能错了),因为有一个限制。宏只能获取代码的一个子树,并生成一个子树来替换它。因此,您不能编写整个程序转换宏,因为这很酷


然而,宏目前仍然可以做很多事情——肯定比任何其他语言都要多。如果您使用的是静态编译,那么进行整个程序转换就不难了,因此限制也就不那么重要了。

Lisp用户将Lisp称为可编程编程编程语言。它用于符号计算-使用符号进行计算

宏只是利用符号计算范式的一种方式。更广泛的观点是Lisp提供了描述符号表达式的简单方法:数学术语、逻辑表达式、迭代语句、规则、约束描述等等。宏(Lisp源格式的转换)只是符号计算的一个应用

这有几个方面:如果你问“重新定义”语言,那么“严格重新定义”意味着重新定义一些现有的语言机制(语法、语义、语用)。但也有语言特征的扩展、嵌入和删除

在Lisp传统中,有很多人试图提供这些特性。Lisp方言和特定实现可能只提供其中的一个子集

重新定义/更改/扩展主要常见Lisp实现提供的功能的几种方法:

  • s-expression语法。s表达式的语法不是固定的。读取器(函数READ)使用所谓的读取表指定读取字符时将执行的函数。可以修改和创建读取表。例如,这允许您更改列表、符号或其他数据对象的语法。还可以为新的或现有的数据类型(如哈希表)引入新语法。也可以完全替换s表达式语法并使用不同的解析机制。如果新的解析器返回Lisp表单,则不需要对解释器或编译器进行任何更改。一个典型的例子是可以读取中缀表达式的读取宏。在这样的read宏中,使用了中缀表达式和运算符的优先规则。读取宏不同于普通宏:读取宏在Lisp数据语法的字符级别上工作

  • 更换功能。顶级函数绑定到符号。用户可以更改此绑定的名称。大多数实现都有一种机制,即使对于许多内置函数也是如此。如果要提供内置多功能厅的替代方案,可以替换其定义。一些实现会引发错误,然后提供继续更改的选项。有时需要解锁一个包。这意味着可以用新的定义替换一般的函数。这是有局限性的。一种是编译器可以在代码中内联函数。要查看效果,需要重新编译使用更改代码的代码

  • 建议功能。通常需要向函数添加一些行为。这在Lisp世界中被称为“建议”。许多常见的Lisp实现都将提供这样的功能

  • 自定义软件包。包在名称空间中对符号进行分组。COMMON-LISP包是所有符号的归宿,这些符号是ANSI COMMON LISP标准的一部分。程序员可以创建新包并导入现有符号。因此,您可以在程序中使用扩展的通用LISP包,该包提供了更多或不同的功能。只需添加(包内的“EXTENDED-COMMON-LISP”),您就可以开始使用自己的COMMON-LISP扩展版本进行开发。根据所使用的名称空间,您使用的Lisp方言看起来可能略有不同,甚至完全不同。在Lisp机器上,通常有几种Lisp方言以这种方式并排出现:ZetaLisp、CLtL1、ANSI Common Lisp和Symbolics Common Lisp

  • CLOS和动态对象。公共Lisp对象系统内置了更改。元对象协议扩展了这些功能。CLOS本身可以在CLOS中扩展/重新定义。你想要不同的继承权。Wri
    ? (cx <circle cx="62" cy="135" r="20"/>) 
    62
    
    (eval-when (:compile-toplevel :load-toplevel :execute)
      (when (and (not (boundp '*Non-XMLISP-Readtable*)) (get-macro-character #\<))
        (warn "~%XMLisp: The current *readtable* already contains a #/< reader function: ~A" (get-macro-character #\<))))