简单的答案是,您可以在延迟评估对功能至关重要的情况下创建新语法。想象一下,您想要一个与if-elseif 工作方式相同的新if。在 Lisp 中,它类似于cond,但没有明确的begin。示例用法是:
(if* (<= 0 x 9) (list x)
(< x 100) (list (quotient x 10) (remainder x 10))
(error "Needs to be below 100"))
不可能将其作为一个过程来实现。我们可以通过要求用户给我们 thunk 来实现相同的工作,因此这些部分将变成可以运行的程序:
(pif* (lambda () (<= 0 x 9)) (lambda () (list x))
(lambda () (< x 100)) (lambda () (list (quotient x 10) (remainder x 10)))
(lambda () (error "Needs to be below 100")))
现在很容易实现:
(define (pif* p c a . rest)
(if (p)
(c)
(if (null? rest)
(a)
(apply pif* a rest))))
这就是 JavaScript 和几乎所有其他不支持宏的语言的做法。现在,如果您想赋予用户制作第一个而不是第二个的能力,您需要宏。您的宏可以将第一个写入第二个,以便您的实现可以是一个函数,或者您可以将代码更改为嵌套 if:
(define-macro if*
(syntax-rules ()
((_ x) (error "wrong use of if*"))
((_ p c a) (if p c a))
((_ p c next ...) (if p c (if* next ...)))))
或者,如果您想使用该过程,只需将每个参数包装在 lambda 中就更简单了:
(define-syntax if*
(syntax-rules ()
((_ arg ...) (pif* (lambda () arg) ...)))
宏的需要是减少样板文件并简化语法。当您看到相同的结构时,您应该尝试将其抽象为一个过程。如果这是不可能的,因为参数以特殊形式使用,您可以使用宏来完成。
Babel,一个 ES6 到 ES5 的转换器,将 JavaSript ES6 语法转换为 ES5 代码。如果 ES5 有宏,那就像制作可兼容宏来支持 ES6 一样容易。事实上,该语言较新版本的大多数特性都是不必要的,因为程序员不必等待一种语言的新版本来赋予它新的花哨特性。如果 Algol 语言(PHP、Java、JavaScript、Python)具有卫生的宏支持,几乎不需要任何新的语言功能。