阅读Paul Graham关于编程语言的文章,你可能会认为Lisp宏是唯一的选择。作为一个忙碌的开发人员,在其他平台上工作,我还没有使用Lisp宏的特权。作为一个想要了解热门话题的人,请解释一下是什么让这个功能如此强大。
请将这一点与我从Python、Java、c#或C开发世界中理解的东西联系起来。
阅读Paul Graham关于编程语言的文章,你可能会认为Lisp宏是唯一的选择。作为一个忙碌的开发人员,在其他平台上工作,我还没有使用Lisp宏的特权。作为一个想要了解热门话题的人,请解释一下是什么让这个功能如此强大。
请将这一点与我从Python、Java、c#或C开发世界中理解的东西联系起来。
当前回答
您将在这里找到关于lisp宏的全面辩论。
这篇文章的一个有趣的子集:
In most programming languages, syntax is complex. Macros have to take apart program syntax, analyze it, and reassemble it. They do not have access to the program's parser, so they have to depend on heuristics and best-guesses. Sometimes their cut-rate analysis is wrong, and then they break. But Lisp is different. Lisp macros do have access to the parser, and it is a really simple parser. A Lisp macro is not handed a string, but a preparsed piece of source code in the form of a list, because the source of a Lisp program is not a string; it is a list. And Lisp programs are really good at taking apart lists and putting them back together. They do this reliably, every day. Here is an extended example. Lisp has a macro, called "setf", that performs assignment. The simplest form of setf is (setf x whatever) which sets the value of the symbol "x" to the value of the expression "whatever". Lisp also has lists; you can use the "car" and "cdr" functions to get the first element of a list or the rest of the list, respectively. Now what if you want to replace the first element of a list with a new value? There is a standard function for doing that, and incredibly, its name is even worse than "car". It is "rplaca". But you do not have to remember "rplaca", because you can write (setf (car somelist) whatever) to set the car of somelist. What is really happening here is that "setf" is a macro. At compile time, it examines its arguments, and it sees that the first one has the form (car SOMETHING). It says to itself "Oh, the programmer is trying to set the car of somthing. The function to use for that is 'rplaca'." And it quietly rewrites the code in place to: (rplaca somelist whatever)
其他回答
lisp宏以程序片段作为输入。这个程序片段被表示为一个数据结构,可以按照您喜欢的任何方式进行操作和转换。最后,宏输出另一个程序片段,这个片段是在运行时执行的。
c#没有宏功能,但是如果编译器将代码解析为CodeDOM树,并将其传递给一个方法,该方法将其转换为另一个CodeDOM,然后将其编译为IL,则会有等效的宏功能。
这可以用来实现“糖”语法,如for each-statement using-clause, linq select-expressions等等,作为转换为底层代码的宏。
如果Java有宏,您就可以在Java中实现Linq语法,而不需要Sun更改基本语言。
下面是在c#中实现lisp风格的宏的伪代码:
define macro "using":
using ($type $varname = $expression) $block
into:
$type $varname;
try {
$varname = $expression;
$block;
} finally {
$varname.Dispose();
}
Lisp宏代表了几乎在任何大型编程项目中都会出现的一种模式。最终,在一个大的程序中,你会有一段代码,你会意识到,如果你写一个程序,把源代码输出为文本,然后你就可以粘贴进去,这会更简单,更不容易出错。
在Python中,对象有两个方法__repr__和__str__。__str__只是人类可读的表示。__repr__返回一个有效的Python代码表示,也就是说,可以作为有效的Python输入解释器。通过这种方式,您可以创建一些小的Python代码片段,生成可以粘贴到实际源代码中的有效代码。
在Lisp中,整个过程由宏系统形式化。当然,它允许您为语法创建扩展并执行各种奇特的事情,但它的实际用处可以从上面总结出来。当然,Lisp宏系统允许您使用整个语言的全部功能来操作这些“片段”是有帮助的。
通用Lisp宏本质上扩展了代码的“语法原语”。
例如,在C语言中,switch/case结构只适用于整型,如果你想将它用于浮点数或字符串,你就只能使用嵌套的if语句和显式比较。你也不可能编写一个C宏来为你做这项工作。
但是,由于lisp宏(本质上)是一个lisp程序,它接受代码片段作为输入,并返回代码来替换宏的“调用”,因此您可以尽可能地扩展您的“原语”库,通常最终会得到一个更可读的程序。
要在C中做同样的事情,您必须编写一个自定义预处理器,它会吃掉您的初始(不完全是C)源代码,并吐出C编译器可以理解的东西。这不是一种错误的方法,但它不一定是最简单的。
一行程序回答:
简单的语法=>宏over表达式=>简洁=>抽象=>强大
Lisp宏只是以编程方式编写代码。也就是说,在展开宏之后,您得到的只不过是没有宏的Lisp代码。所以,原则上来说,他们没有什么新成就。
然而,它们与其他编程语言中的宏不同,它们在表达式级别上编写代码,而其他宏在字符串级别上编写代码。这在lisp中是独一无二的,这要感谢他们的括号;或者更准确地说,它们的最小语法多亏了括号。
正如本文中的许多示例所示,以及Paul Graham的On Lisp, Lisp宏可以成为使代码更加简洁的工具。当简洁性达到一定程度时,它为代码提供了新的抽象级别,使其更加简洁。回到第一点,原则上他们没有提供任何新的东西,但这就像说既然纸和铅笔(几乎)组成了图灵机,我们就不需要真正的计算机。
如果你懂点数学,想想为什么函子和自然变换是有用的想法。原则上,它们没有提供任何新东西。然而,通过将它们扩展到较低层次的数学中,你会发现几个简单想法的组合(就范畴理论而言)可能需要10页才能写下来。你喜欢哪一种?
In python you have decorators, you basically have a function that takes another function as input. You can do what ever you want: call the function, do something else, wrap the function call in a resource acquire release, etc. but you don't get to peek inside that function. Say we wanted to make it more powerful, say your decorator received the code of the function as a list then you could not only execute the function as is but you can now execute parts of it, reorder lines of the function etc.