《Clojure编程乐趣》—— 第1章,第1.2节为何(又一种)Lisp

简介:

本节书摘来自异步社区《Clojure编程乐趣》一书中的第1章,第1.1节1.2 为何(又一种)Lisp,作者 【美】Michael Fogus , Chris Houser,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.2 为何(又一种)Lisp
Clojure编程乐趣
一套好的概念可以将大脑从无用功中解放出来,专注于更高级的问题。

—Alfred North Whitehead

去到任何一个开源项目托管站点,搜索“Lisp interpreter”(Lisp解析器)。这个貌似平淡无奇的搜索,可能会带给我们一个堆积如山1**的结果。事实上,计算机科学的历史中堆积着大量废弃的Lisp实现(Fogus 2009)。诸多初衷良好的Lisp来了又走,一路遭到无数嘲笑,但到了明天,搜索结果依然会无限制增长。既然有如此惨痛的过往,为何还有人愿意将其崭新的程序设计语言构建于Lisp模型之上呢?

1.2.1 优美
Lisp吸引了计算机科学史上最聪明的一群头脑。但是,仅有权威的争论是不够的,我们不该仅凭此判断Lisp。只有用其编写应用,才可以直接看得到Lisp语言家族的真正价值。Lisp的风格就在于极具表现力、非常实用,以及在大多数情况下表现出的美感。最初的Lisp语言是John McCarthy在其惊天动地的论文“Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I”(McCarthy 1960)中定义的,只用区区7个函数和两个特殊form便定义出整个语言:atom、car、cdr、cond、cons、eq、quote、lambda和label。

通过这9个form的组合,McCarthy将整个计算以一种令人窒息的方式呈现出来。计算机程序员总在寻找美,而多数情况下,美会以简单的形式自我呈现出来。7个函数2个特殊form,美无过于此。

1.2.2 极度灵活
Lisp何以历经五十余年而弥新,相较之下,无数语言却成了匆匆过客?个中原因可能极尽复杂,但究其根因,无外乎Lisp自身的语言基因(Tarver 2008)将语言的灵活性推向极致。Lisp新手常常气馁,无处不在的括号和前缀记法,与非Lisp程序设计语言大相径庭。然而,正是这种行为上的规律性,不仅让需要记忆的语法规则减少了,也让宏的编写变得很简单。我们会在第8章更详细地了解宏,但为了让你开开胃,这里先简单地看一下。下面是一个例子,稍后再来细究:


652f418fc4db8d8e4b219bd6eb4e72f290ead9a6

希望你对这些单词有所了解,因为这不是一本SQL的书。这里要说的是,Clojure并没有内建对SQL的支持。SELECT、FROM等这些词并不是内建的form。它们也不是常规的函数,如果SELECT是,那么使用a、b和c就错了,因为它们还没有定义。

如何用Clojure定义这样的领域特定语言(domain-specific language,DSL)呢?好吧,这不是产品就绪(production-ready)的代码,没有绑定到真实的数据库服务器上;但只要有了程序1.1列出的一个宏和三个函数,前面的查询就能够返回下面这些实际的值:


e7908793e2a1caed7810f706b77ece039ef21ea3

请注意,FROM和ON这样的词是从输入表达式中直接取出来的,而其他诸如~max和AND则要特殊对待。调用查询时,max得到一个5,这是从字面量SQL字符串中提取的,由一个单独向量提供,以这种方式准备的查询颇为完美,可以免受SQL注入攻击。AND form由Clojure的前缀表达式转成SQL所需的中缀表达式。

程序1.1 以Clojure编写领域特定语言,用以嵌入SQL查询


461811e57598376e94ea3ae5c326e615363a6a1d

但需要指出的是,这算不上是一种很好的SQL DSL—还有实现更为完整的。2我们要说的是,一旦懂得了这种创建DSL的技巧,就可以识别出一些机会,定义自己的DSL,解决比SQL更窄的、更加面向应用的问题。无论是给不常见的非SQL数据库提供查询语言,还是给模糊的数学学科提供一种方式表现函数,抑或是处理其他自己都未曾想过的应用,能够拥有如此灵活易扩展的基础语言,且不损伤语言自身特性,都将成为游戏规则的改变者。

虽然我们不该太过深入细节地讨论实现,但还是要顺着之前讨论过的一些重要方面,简单看看列表1.1的实现。

自下而上阅读,首先映入眼帘的是入口点,SELECT宏。它返回的是一个有两项的vector—第一项通过调用 expand-clause生成,返回的是一个经过转换的查询字符串,而第二项是另一个vector,表示输入里由~标记的表达式。~表示反quote,我们会在第8章讨论其更常见的用法。另外要注意的是这里用到的tree-seq,通过它可以很容易地将感兴趣的项从值树(也就是输入表达式)上提取出来。

expand-clause函数用语句的第一个词,在clause-map里进行了查询,然后,调用适当的函数,完成从Clojure的s表达式(s-expression)到SQL字符串的转换。clause-map为SQL表达式各个部分提供了所需的详细功能:插入逗号或是其他SQL语法,有时还要递归调用expand-clause进行子语句的转换。其中之一是WHERE语句,通过委托给expand-expr函数,处理了SQL所需的前缀表达式到中缀表达式的通用转换。

总的来说,这个例子展示的Clojure灵活性大多是因为宏可以接受代码form(比如前面展示的这个SQL DSL的例子),并将其当做数据对待—遍历树、转换值等。之所以可以这样做,不只是因为代码可以当做数据,还因为在Clojure程序里,代码就是数据。

1.2.3 代码即数据
“代码即数据”这样的说法最初很难理解。实现一门程序设计语言,代码同数据一般对待,这需要语言本身具有非常强的可塑性。当语言就是以这种本质的数据结构表现时,语言本身就可以操作自己的结构和行为了(Graham 1995)。读到上面这句话,我们脑海中可能会浮现出一条衔尾蛇(Ouroboros)3,也许这么说不合适,因为Lisp可以比作一个自我舔食的棒棒糖—更正规的说法是同像性(homoiconicity)。要完全掌握Lisp的同像性,需要跨越一个巨大的概念鸿沟,但在本书里,我们尽力帮你理解这个概念,希望你最终能够领会其巨大的威力。

初学Lisp是一番乐趣,如果你能从本书得到同样的体验,那么我们欢迎你—甚至有点嫉妒。

1……且疯狂的。
2要提到的一个是ClojureQL,它位于http://gitorious.org/clojureql
3译注:衔尾蛇(Ouroboros)是自古流传至今的一种符号,大致形象是一条蛇正吞食自己的尾巴,结果形成了一个圆环。

相关文章
|
Java PHP 开发工具
编程语言Clojure入门
在众多的编程语言中,不少开发人员熟悉Java、C#、PHP等。但是很早以前,也有一些小众的语言,比如Lisp语言,它是一种适用于符号处理和自动推理的编程语言,内部使用表结构来表达非数值计算。而Clojure语言是在JVM上实现的Lisp风格的语言,语法与Lisp类似,且可以和Java语言进行互操作
1146 0
编程语言Clojure入门
|
Rust
Rust 语言基础 | 学习语言都应该快速得出印象
Rust 语言基础 | 学习语言都应该快速得出印象
134 0
Rust 语言基础 | 学习语言都应该快速得出印象
|
Shell BI 测试技术
Haskell 编程入门
在过去的几个月里,学习Haskell让我觉得非常快乐,但是入门的过程并没有我原先想象的那么简单。我非常幸运地在一个正确的地方工作,并且因此能够在Facebook参加Bryan O'Sullivan的Haskell课程。在Try Haskell上玩了一段时间后,最终你就会想要在自己的电脑上安装GHC了。
175 0
Haskell 编程入门
|
Java
《Clojure程序设计》——导读
本节书摘来自异步社区《Clojure程序设计》一书中的导读,作者 【美】Stuart Halloway , Aaron Bedra,更多章节内容可以访问云栖社区“异步社区”公众号查看
2006 0