创造其它语言的语言 - Lisp

如果我们想用操作数据一样来操作代码, 答案就是:把代码变成数据。

如果代码必须是数据,用什么格式来表示它?

XML可以!

JSON也可以!

你自定义的其他格式也可以!

但是,如果我们努力去寻找最简单的数据结构,我们会达到最终的目的地:列表(List)。

实际上,我们的JSON数组语言是由JavaScript实现的,两者是融为一体的,想对JSON数组语言增加语法,必须要写对应的JavaScript代码。

但是对Lisp来说,它的代码本身就是数据,此外Lisp还支持宏(macro),宏可以直接把列表当做数据来操作,从而生成新代码,不需要其他语言的介入。

这是个强大得让人恐怖的功能。

你可以想象,你可以和语言设计者站在同一个层次去增强一门语言,把那个语言变成一个属于你所在业务领域的语言,然后用新语言去编程,那编程速度得有多快!

但是,Lisp宏要求在一个更高的层面进行编程,把代码当做数据折腾来折腾去,感觉就是一个人肉编译器了,没有多少程序员能够做得很好。

如果你刚开始学习的时候,主要是忍受着它那前缀表达式和大量的括号,去学习它的函数式编程,这时候没觉得 Lisp 有多厉害,等到学到了 宏(macro)这个主题的时候,就被 Lisp 的变态的能力真正地震撼了:

这不是一门简单的编程语言,它是一门创造其他语言的语言。

Lisp 在制造别的语言(DSL)时是如此的成功,以至于它最终走向了 “失败”。

很多语言解决问题的思路是分而治之:把一个大任务拆分成一个个小任务,再把小任务拆分成更小的任务,然后去编码实现。

Lisp 则有着截然不同的思路,由于其强大的能力,Lisp 程序员倾向于改造 Lisp,把这门语言改造成一个问题领域相关的语言,即 DSL ,然后用这个 DSL 来轻松编程,去解决问题。

当然这个改造的过程是个渐进式的:

“在编程的时候你可能会想 ‘Lisp 要是有这样或者那样的操作符就好了。’ 那你就可以直接去实现它。之后,你会意识到使用新的操作符也可以简化程序中另一部分的设计,如此种种。语言和程序一同演进。就像交战两国的边界一样,语言和程序的界限不断地移动,直到最终沿着山脉和河流确定下来,这也就是你要解决的问题本身的自然边界。最后你的程序看起来就好像语言就是为解决它而设计的。并且当语言和程序彼此都配合得非常完美时,你得到的将是清晰、简短和高效的代码。”
— Paul Graham 《On Lisp》。

这种使用 DSL 去解决问题的方法有什么问题呢?

碎片化! 会出现很多 “小语言”,这些语言之间有细微的不同,这就是为什么你的 Lisp 代码对别人来说读起来很吃力的原因。

而其他语言则不存在这个问题,相对容易去理解代码的含义。Lisp, 由于其变态的表达能力, 一个符号可能是个变量,函数,操作符。你需要花费大量时间去阅读代码才能搞清楚它到底是什么含义,这就太悲催了。

Lisp 的 “失败” 的一大原因就是它的碎片化,而碎片化又源于语言本身的特性和它那用 DSL 解决问题的风格。

我在说 “失败” 的时候,一直用引号, Lisp 真的失败了吗?No!Lisp 的思想已经进入到了现在主流的语言中,无论是 Python,JavaScript,甚至 Java 都具备函数式编程的能力,还有像 Scala 这样既能 OOP,又能 FP 的语言。
但是,Lisp 那强大的宏,那运行时改变自身的能力并没有被其他语言接受,Ruby 比较接近,但是差得还很远。可能大家害怕这个双刃剑了吧!

原文链接:https://stopa.io/post/265  

翻译有删减 为什么Lisp程序员总能碾压其他人?

https://blog.csdn.net/weixin_43314519/article/details/107754746

猜你喜欢

转载自blog.csdn.net/qq_42672770/article/details/127379505