LISPmacros可以走多远?
我已经读了很多,LISP可以重新定义语法,大概是用macros。 我很好奇这实际上走了多远? 你能否重新定义语言结构,使其成为另一种语言的编译器? 例如,你能否将LISP的function性改变成面向对象的语法和语义,也许是说语法更接近Ruby这样的语法?
特别是,有可能使用macros摆脱括号的地狱? 我已经学会了足够多的(Emacs-)LISP来自定义Emacs的微特性,但是我非常好奇macros可以在定制语言的时候走多远。
这是一个非常好的问题。
我认为这是微妙的,但绝对可以回答:
macros不会卡在sexpression式中。 查看使用关键字(符号)编写的非常复杂的语言的LOOPmacros。 所以,虽然你可以用圆括号开始和结束循环,但是它里面有它自己的语法。
例:
(loop for x from 0 below 100 when (even x) collect x)
这就是说,最简单的macros只是使用sexpression式。 而且你会“卡住”使用它们。
但是像塞尔吉奥回答的那样,表情开始感觉不错。 语法不复存在,并开始在语法树中进行编码。
至于读者macros,是的,你可以想象这样写:
#R{ ruby.code.goes.here }
但是你需要编写你自己的Ruby语法分析器。
您也可以使用编译为现有Lisp构造的macros来模拟一些Ruby构造,如块。
#B(some lisp (code goes here))
会翻译成
(lambda () (some lisp (code goes here)))
看到这个页面如何做到这一点。
是的,您可以重新定义语法,以便Lisp成为编译器。 你使用“Readermacros”来做到这一点,它与你可能想到的普通的“Compilermacros”不同。
Common Lisp拥有内置的工具来为读者和读者macros定义新的语法来处理语法。 这个处理是在读取时完成的(在编译或者eval时间之前)。 要了解有关在Common Lisp中定义读者macros的更多信息,请参阅Common Lisp Hyperspec – 您将需要阅读Ch。 2,“语法”和Ch。 23,“读者” (我相信Scheme有相同的function,但我不太熟悉它 – 请参阅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,{就像一个(而且那个)就像一个)。 然后你创build一个函数( lcurly-brace-reader
),当读者看到一个{,并且你使用set-macro-character
把这个函数分配给{ 然后你告诉Lisp(和)就像[和](也就是说,没有意义的语法)。
你可以做的其他事情包括,例如, 创build一个新的string语法,或者使用[和]来包含内附符号表示法,并将其处理成Sexpression式。
你也可以远远超出这个范围,用你自己的macros观angular色重新定义整个语法,这将触发读者的行为,所以天空真的是极限。 这只是Paul Graham和其他人一直说Lisp是编写编译器的好语言的原因之一。
我不是一个Lisp专家,我甚至不是一个Lisp程序员,但经过一段时间的语言实验之后,我得出了一个结论:在一段时间之后,括号开始变成“隐形”,你开始看到代码为你想要它。 您开始更加关注通过s-exprs和macros创build的语法结构,而不是列表和括号的文本forms。
如果你利用一个好的编辑器来帮助缩进和语法着色(尝试将圆括号设置为与背景非常相似的颜色),那么这是特别真实的。
您可能无法完全replace语言并获得“Ruby”语法,但您并不需要它。 由于语言的灵活性,如果你愿意的话,你可以结束一种方言,就像你正在遵循“Ruby风格的编程”一样,无论这对你来说意味着什么。
我知道这只是一个经验性的观察,但我认为我有一个Lisp启蒙时刻,当我意识到这一点。
Lisp的新手想要“摆脱所有的括号”。 它持续几个星期。 在通常的Sexpression式parsing器之上,没有任何项目能够build立一个严肃的通用编程语法,因为程序员不可避免地喜欢你目前认为的“括号地狱”。 这需要一点习惯,但不是太多! 一旦你习惯了,你可以真正的体会到默认语法的可塑性,回到那些只有一种方式来expression任何特定的编程结构的语言是真正的光栅。
这就是说,Lisp是构build域特定语言的一个很好的基础。 和XML一样好,甚至更好。
祝你好运!
我见过的Lispmacros的最好的解释是
https://www.youtube.com/watch?v=4NO83wZVT0A
约55分钟英寸开始。这是一个“实用的通用Lisp”的作者,彼得Seibel,这是最好的Lisp教科书的演讲video。
Lispmacros的动机通常很难解释,因为在一个简单的教程中,它们太长而无法呈现。 彼得拿出一个很好的例子。 您可以完全掌握它,并且可以很好地正确使用Lispmacros。
你问:“你可以改变LISP的function性为一个更面向对象的语法和语义”。 答案是肯定的。 事实上,Lisp原本没有任何面向对象的编程,这并不奇怪,因为在面向对象编程之前,Lisp就已经存在了。 但是当我们在1978年第一次得知OOP的时候,我们可以很容易地将它添加到Lisp中,使用macros等。 最终开发了Common Lisp对象系统(CLOS),这是一个非常强大的面向对象的编程系统,可以很好地适应Lisp。 整个事情可以作为一个扩展加载 – 没有内置! 这一切都是用macros来完成的。
Lisp有一个完全不同的特性,称为“阅读器macros”,可以用来扩展语言的表面语法。 使用阅读器macros,可以使子语言具有C语言或类似Ruby的语法。 他们在内部将文本转换为Lisp。 这些并没有被大多数真正的Lisp程序员广泛使用,主要是因为很难扩展交互式开发环境来理解新的语法。 例如,Emacs缩进命令会被新语法混淆。 如果你精力充沛,Emacs也是可扩展的,你可以教你关于你的新的词法语法。
常规macros对对象列表进行操作。 通常,这些对象是其他列表(因此形成树)和符号,但它们可以是其他对象,如string,哈希表,用户定义的对象等。这些结构被称为s-exps 。
所以,当你加载一个源文件时,你的Lisp编译器将parsing文本并产生s-exps。 macros对这些进行操作。 这很好用,这是在s-exps的精神范围内扩展语言的一个奇妙的方法。
另外,前面提到的parsing过程可以通过“读取器macros”进行扩展,使您可以自定义编译器将文本转换为s-exps的方式。 然而,我build议你接受Lisp的语法,而不是将其弯曲到别的东西上。
当你提到Lisp的“function性”和Ruby的“面向对象的语法”时,你听起来有些困惑。 我不确定应该使用什么“面向对象的语法”,但是Lisp是一种多范式的语言,它非常好地支持面向对象的编程。
顺便说一下,当我说Lisp时,我的意思是Common Lisp 。
我build议你放弃你的偏见,并给Lisp一个诚实的去 。
你所要求的有点像问问如何成为专家的巧克力蛋糕,以便你可以从你最喜欢的巧克力蛋糕中去除所有那些地狱般的褐色蛋糕。
括号地狱? 在我看到没有更多的括号:
(function toto)
比在:
function(toto);
而且,
(if tata (toto) (titi) (tutu))
不超过:
if (tata) toto(); else { titi(); tutu(); }
我看到更less的括号和“;” 虽然。
是的,你可以从根本上改变语法,甚至可以逃避“括号里的地狱”。 为此,您将需要定义一个新的阅读器语法。 看看阅读器macros。
然而,我怀疑为了达到Lisp的专业水平,编写这样的macros,你需要沉浸在语言中,以至于你不会再考虑括号“地狱”。 也就是说,当你知道如何避免他们的时候,你会接受他们作为一件好事。
如果你想Lisp看起来像Ruby使用Ruby。
可以像使用Ruby(和Python)一样以非常简单的方式使用Ruby,这是他们如此迅速获得接受的主要原因之一。
@sparkes
有时LISP是明确的语言select,即Emacs扩展。 我确定我可以用Ruby来扩展Emacs,但是Emacs被devise成可以用LISP扩展,所以在这种情况下使用它似乎是有意义的。
请参阅这个例子,了解如何使用XML模板这样的复杂任务来扩展lisp阅读器。
http://common-lisp.net/project/cl-quasi-quote/present-class.html
此用户库在编译时将XML的静态部分编译为UTF-8编码的文字字节数组,以准备将其写入networkingstream中。 并且它们在正常的lispmacros中可用,它们是正交的…逗号字符的放置影响哪些部分是恒定的,哪些应该在运行时进行评估。
更多详情请访问: http : //common-lisp.net/project/cl-quasi-quote/
Common Lisp语法扩展的另一个项目: http : //common-lisp.net/project/cl-syntax-sugar/
这是一个棘手的问题。 由于lisp在结构上已经非常接近parsing树,因此大量的macros和在parsing器生成器中实现自己的迷你语言的区别并不十分清楚。 但是,除了开幕式和闭幕式之外,你可以很容易地得到一些看起来不像lisp的东西。
其中一个引起我头脑的macros的用法是对DB的SQL请求进行编译时validation。
一旦意识到自己在编译时已经掌握了全部的语言,就会开启有趣的新视angular。 这也意味着你可以用有趣的新方式(比如渲染编译不可重现,可以很容易地变成debugging噩梦)在自己的脚下进行拍摄。