LISP宏可以走多远?

时间:2008-08-05 07:32:54

标签: macros lisp

我已经阅读了很多内容,LISP可以动态地重新定义语法,大概是宏。我好奇这到底有多远?你能重新定义语言结构,以至于它的边界成为另一种语言的编译器吗?例如,您是否可以将LISP的功能特性更改为更面向对象的语法和语义,或许可以说语法更接近像Ruby这样的东西?

特别是,是否可以使用宏来摆脱括号地狱?我已经学会了足够的(Emacs-)LISP来使用我自己的微功能定制Emacs,但我很好奇宏可以在定制语言方面走多远。

14 个答案:

答案 0 :(得分:34)

这是一个非常好的问题。

我认为这是微妙的,但绝对是可以回答的:

宏不会卡在s表达式中。有关使用关键字(符号)编写的非常复杂的语言,请参阅LOOP宏。所以,虽然你可以用括号开始和结束循环,但它内部却有自己的语法。

示例:

(loop for x from 0 below 100
      when (even x)
      collect x)

话虽如此,大多数简单的宏只使用s表达式。并且你会被“卡住”使用它们。

但像塞尔吉奥所回答的那样,s表达开始感觉正确。语法不受影响,您可以在语法树中开始编码。

至于读者宏,是的,你可以想象写下这样的东西:

#R{
      ruby.code.goes.here
  }

但是你需要编写自己的Ruby语法解析器。

您还可以使用编译为现有Lisp构造的宏来模仿某些Ruby构造(如块)。

#B(some lisp (code goes here))

会转换为

(lambda () (some lisp (code goes here)))

请参阅this page了解如何操作。

答案 1 :(得分:20)

是的,您可以重新定义语法,以便Lisp成为编译器。您可以使用“Reader Macros”执行此操作,这与您可能正在考虑的常规“编译器宏”不同。

Common Lisp具有内置工具,可以为读取器和读取器宏定义新语法来处理该语法。此处理在读取时间(在编译或eval时间之前)完成。要了解有关在Common Lisp中定义阅读器宏的更多信息,请参阅Common Lisp Hyperspec - 您需要阅读Ch. 2, "Syntax"Ch. 23, "Reader"。 (我相信Scheme具有相同的功能,但我对它不熟悉 - 请参阅Scheme sourcesArc programming language。)

作为一个简单的例子,让我们假设您希望Lisp使用花括号而不是括号。这需要类似以下读者定义:

;; { and } become list delimiters, along with ( and ).
(set-syntax-from-char #\{ #\( )
(defun lcurly-brace-reader (stream inchar) ; this was way too easy to do.
  (declare (ignore inchar))
  (read-delimited-list #\} stream t))
(set-macro-character #\{ #'lcurly-brace-reader)

(set-macro-character #\} (get-macro-character #\) ))
(set-syntax-from-char #\} #\) )

;; un-lisp -- make parens meaningless
(set-syntax-from-char #\) #\] ) ; ( and ) become normal braces
(set-syntax-from-char #\( #\[ )

你告诉Lisp {就像一个(而且}就像一个)。然后你创建一个函数(lcurly-brace-reader),读者在看到{时会调用它,并使用set-macro-character将该函数分配给{。然后你告诉Lisp(和)就像[和](也就是说,没有意义的语法)。

您可以执行的其他操作包括,例如creating a new string syntax或使用[和]括起in-fix符号并将其处理为S表达式。

您还可以远远超出此范围,使用您自己的宏字符重新定义整个语法,这些宏字符将触发阅读器中的操作,因此天空确实是极限。这只是Paul Grahamothers一直说Lisp是编写编译器的好语言的原因之一。

答案 2 :(得分:16)

我不是Lisp专家,哎呀我甚至不是一个Lisp程序员,但经过一些语言实验后,我得出的结论是,一段时间后,括号开始变成'隐形'而你开始看到你想要的代码。您开始更多地关注通过s-exprs和宏创建的语法结构,而不是注意列表和括号文本的词法形式。

如果您利用一个有助于缩进和语法着色的优秀编辑器(尝试将括号设置为与背景非常相似的颜色),则尤其如此。

您可能无法完全替换语言并获得“Ruby”语法,但您不需要它。由于语言的灵活性,你可以找到一种方言,如果你愿意的话,就会感觉你正在遵循“红宝石编程风格”,无论那对你意味着什么。

我知道这只是一个经验观察,但我认为当我意识到这一点时,我有一个Lisp启蒙时刻。

答案 3 :(得分:15)

一遍又一遍,Lisp的新手想要“摆脱所有的括号”。它持续数周。没有任何项目可以在通常的S表达式解析器之上构建一个严格的通用编程语法,因为程序员总是最喜欢你当前认为的“括号地狱”。它需要一点点习惯,但不是很多!一旦你习惯了它,你就可以真正体会到默认语法的可塑性,回到只有一种表达任何特定编程结构的方式的语言真的很光。

话虽如此,Lisp是构建领域特定语言的绝佳基础。与XML一样好,如果不是更好。

祝你好运!

答案 4 :(得分:12)

我见过的Lisp宏的最佳解释是

https://www.youtube.com/watch?v=4NO83wZVT0A

从大约55分钟开始。这是“实用公共Lisp”的作者Peter Seibel给出的一个视频,这是最好的Lisp教科书。

Lisp宏的动机通常很难解释,因为它们在一个过于冗长而无法在简单教程中出现的情况下真正发挥作用。彼得提出了一个很好的榜样;你可以完全掌握它,它可以很好地正确使用Lisp宏。

你问:“你能否将LISP的功能性改变为更面向对象的语法和语义”。答案是肯定的。事实上,Lisp最初根本没有任何面向对象的编程,因为Lisp在面向对象编程之前就已存在,所以并不奇怪!但是当我们在1978年第一次了解OOP时,我们能够轻松地将它添加到Lisp中,其中包括宏。最终开发了Common Lisp对象系统(CLOS),这是一个非常强大的面向对象编程系统,可以很好地适应Lisp。整个东西可以作为扩展加载 - 没有任何内置!这一切都是用宏完成的。

Lisp有一个完全不同的功能,称为“阅读器宏”,可用于扩展语言的表面语法。使用读取器宏,您可以创建具有类似C或类似Ruby的语法的子语言。他们在内部将文本转换为Lisp。大多数真正的Lisp程序员并没有广泛使用它们,主要是因为很难扩展交互式开发环境以理解新语法。例如,Emacs缩进命令会被新语法混淆。但是,如果你精力充沛,Emacs也是可扩展的,你可以教它新的词法语法。

答案 5 :(得分:11)

常规宏对对象列表进行操作。最常见的是,这些对象是其他列表(因此形成树)和符号,但它们可以是其他对象,如字符串,哈希表,用户定义的对象等。这些结构称为s-exps

因此,当您加载源文件时,您的Lisp编译器将解析文本并生成s-exps。宏对这些进行操作。这很有效,这是在s-exps精神范围内扩展语言的绝佳方式。

此外,上述解析过程可以通过“读取器宏”进行扩展,使您可以自定义编译器将文本转换为s-exps的方式。但是,我建议你接受Lisp的语法,而不是把它弯曲成别的东西。

当你提到Lisp的“功能性”和Ruby的“面向对象语法”时,你听起来有点困惑。我不确定应该是什么“面向对象的语法”,但Lisp是一种多范式语言,它支持面向对象的编程 extremelly

顺便说一句,当我说Lisp时,我的意思是Common Lisp

我建议你放弃偏见并give Lisp an honest go

答案 6 :(得分:10)

括号地狱?我看到没有更多的括号:

(function toto)

而不是:

function(toto);

并在

(if tata (toto)
  (titi)
  (tutu))

不超过:

if (tata)
  toto();
else
{
  titi();
  tutu();
}

我看到更少的括号和';'虽然。

答案 7 :(得分:9)

你所问的有点像问如何成为一名专家巧克力制作者,这样你就可以从你最喜欢的巧克力蛋糕中去掉所有那些地狱般的褐色东西。

答案 8 :(得分:6)

是的,你可以从根本上改变语法,甚至逃避“括号地狱”。为此,您需要定义新的阅读器语法。查看读者宏。

我确实怀疑为了达到Lisp专业知识的水平来编程这样的宏,你需要让自己沉浸在语言中,以至于你将不再考虑括号“地狱”。即当你知道如何避免它们时,你会接受它们作为一件好事。

答案 9 :(得分:2)

如果你希望lisp看起来像Ruby使用Ruby。

可以以非常类似的方式使用Ruby(和Python),这是他们如此迅速地获得认可的主要原因之一。

答案 10 :(得分:2)

请参阅此示例,了解读者宏如何使用XML模板等复杂任务扩展lisp阅读器:

http://common-lisp.net/project/cl-quasi-quote/present-class.html

用户库在编译时将XML的静态部分编译为UTF-8编码的文字字节数组,这些数组已准备好写入网络流中。它们可以在普通的lisp宏中使用,它们是正交的...逗号字符的位置会影响哪些部分是常量,哪些部分应该在运行时进行评估。

更多详细信息,请访问:http://common-lisp.net/project/cl-quasi-quote/

另一个针对Common Lisp语法扩展的项目:http://common-lisp.net/project/cl-syntax-sugar/

答案 11 :(得分:1)

@sparkes

有时LISP是明确的语言选择,即Emacs扩展。如果我愿意的话,我确信我可以使用Ruby来扩展Emacs,但是Emacs的设计是为了扩展LISP,所以在这种情况下使用它似乎是有道理的。

答案 12 :(得分:1)

这是一个棘手的问题。由于lisp已经在结构上非常接近解析树,因此大量宏与在解析器生成器中实现自己的迷你语言之间的区别并不十分清楚。但是,除了开幕式和闭幕式之外,你可以很容易地得到一些看起来不像lisp的东西。

答案 13 :(得分:1)

令我烦恼的宏的一个用途是针对DB的SQL请求的编译时验证。

一旦你意识到你在编译时掌握了完整的语言,就会开启有趣的新视角。这也意味着你可以用有趣的新方式射击自己(比如渲染编译不可重复,很容易变成调试噩梦)。