50

我读过很多关于 LISP 可以动态重新定义语法的内容,大概是使用宏。我很好奇这实际上能走多远?你能重新定义语言结构,让它成为另一种语言的编译器吗?例如,您是否可以将 LISP 的功能性质更改为更面向对象的语法和语义,或者说具有更接近于 Ruby 之类的语法?

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

4

14 回答 14

36

这是一个非常好的问题。

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

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

例子:

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

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

但是,就像 Sergio 回答的那样,s 表达式开始感觉正确。语法不碍事,您开始在语法树中编码。

至于阅读器宏,是的,你可以想象这样写:

#R{
      ruby.code.goes.here
  }

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

您还可以使用编译成现有 Lisp 结构的宏来模仿一些 Ruby 结构,例如块。

#B(some lisp (code goes here))

将转化为

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

请参阅此页面了解如何操作。

于 2008-09-15T15:07:21.940 回答
24

是的,你可以重新定义语法,让 Lisp 成为编译器。您可以使用“阅读器宏”来执行此操作,这与您可能正在考虑的普通“编译器宏”不同。

Common Lisp 具有为阅读器和阅读器宏定义新语法以处理该语法的内置工具。此处理在读取时完成(在编译或评估时间之前)。要了解有关在 Common Lisp 中定义阅读器宏的更多信息,请参阅 Common Lisp Hyperspec——您需要阅读Ch. 2,“语法”Ch。23、《读者》。(我相信 Scheme 具有相同的功能,但我不太熟悉它——请参阅 Arc 编程语言的Scheme源代码

举个简单的例子,假设您希望 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 { 就像 a ( 并且 } 就像 a )。然后,您创建一个函数 ( lcurly-brace-reader),读者在看到 { 时将调用该函数,并使用set-macro-character该函数将该函数分配给 {。然后你告诉 Lisp ( 和 ) 就像 [ 和 ] (也就是说,没有意义的语法)。

您可以做的其他事情包括,例如,创建新的字符串语法或使用 [ 和 ] 来包含中缀符号并将其处理为 S 表达式。

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

于 2008-09-16T19:28:28.607 回答
16

我不是 Lisp 专家,哎呀,我什至不是 Lisp 程序员,但在对该语言进行了一些试验后,我得出的结论是,一段时间后括号开始变得“​​不可见”,你开始看到代码为你希望它成为。您开始更多地关注通过 s-exprs 和宏创建的句法结构,而不是列表和括号文本的词汇形式。

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

您可能无法完全替换该语言并获得“Ruby”语法,但您不需要它。多亏了语言的灵活性,如果你愿意,你可以最终拥有一种感觉就像你正在遵循“Ruby 编程风格”的方言,无论这对你意味着什么。

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

于 2008-08-26T09:49:02.323 回答
15

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

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

祝你好运!

于 2008-09-16T19:29:54.763 回答
14

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

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

从大约 55 分钟开始。这是“实用通用 Lisp”的作者 Peter Seibel 的演讲视频,这是最好的 Lisp 教科书。

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

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

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

于 2008-10-05T15:03:31.263 回答
11

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

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

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

当您提到 Lisp 的“函数性质”和 Ruby 的“面向对象的语法”时,您听起来有点困惑。我不确定“面向对象的语法”应该是什么,但 Lisp 是一种多范式语言,它非常好地支持面向对象的编程。

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

我建议你放下偏见,诚实地尝试 Lisp

于 2008-08-26T09:36:27.020 回答
11

括号地狱?我看不到括号:

(function toto)

比在:

function(toto);

而在

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

不超过:

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

我看到更少的括号和';' 尽管。

于 2008-09-16T16:23:18.287 回答
9

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

于 2008-09-17T12:14:53.087 回答
6

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

然而,我确实怀疑要达到 Lisp 专业水平来编写此类宏,您需要将自己沉浸在语言中,以至于您将不再考虑括号“地狱”。也就是说,当你知道如何避免它们时,你就会接受它们是一件好事。

于 2008-09-15T12:07:06.287 回答
2

如果你想让 lisp 看起来像 Ruby,请使用 Ruby。

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

于 2008-08-05T07:40:26.980 回答
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/

于 2008-09-17T00:30:43.247 回答
1

@sparkes

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

于 2008-08-05T07:45:14.020 回答
1

这是一个棘手的问题。由于 lisp 在结构上已经非常接近解析树,因此大量宏和在解析器生成器中实现自己的迷你语言之间的区别不是很清楚。但是,除了开头和结尾的括号外,您很容易得到看起来一点也不像 lisp 的东西。

于 2008-08-06T12:14:12.213 回答
1

让我大吃一惊的宏的用途之一是针对 DB 的 SQL 请求的编译时验证。

一旦你意识到你在编译时手头有完整的语言,它就会打开有趣的新视角。这也意味着您可以以有趣的新方式自爆(比如渲染编译不可重现,这很容易变成调试的噩梦)。

于 2008-09-19T10:43:32.133 回答