Clojure 之美

jopen 9年前

最近,我在博客上有点安静。部分原因是正在学习程序设计语言 Clojure,这是很让人激动的事。

讨厌 Lisp 的人

Clojure 在 2007 左右就被推出了。因此,我是花了一段时间在兴奋中去探索它的。事实上,我在 2013就买了本《The Joy of Clojure1》。是什么让我花了这么长的时间去阅读它呢?

我承认,自从我在大学第一次接触它们的时候我就很讨厌 Lisp 。当我说“讨厌”的时候,我不是在夸大其词。我非常讨厌 Lisps。我就不理解了,为什么还有人想要使用 Lisp 写程序,还给出了替代品。我从未从括号和前缀表达符事情中解脱出来。

然而,Clojure 开发者声称 Clojure 是好的不能再好的东西,是这个星球上最有效的程序设计语言,这些才让我走出了以前的阴影(过渡括号的恐惧)

极其恐怖的括号

如果你看看周围各种 Clojure 文章你将发现他们正在淡化 Lisp 括号和前缀符号在可读性方面的影响。就我个人而言,我想那是无知的。至少对于这个语言的新特性来说。

我们绝大多数的人都是伴随着从左到右阅读的语言和用中缀表示法表示数学和逻辑操作的一般表示方法的语言。这已经根深蒂固的植入我们得血液里。s-expressions2   的使用和 他们作为结果的前缀符合在很多方面是 Clojure 的秘密调料。然而,它要求你从右到左,从里到外的阅读。这对我们大多数人来说有点拧巴,是煎熬。这是进入 Lisp 世界的一个很大的障碍。

例如,这是一些用 Clojure 实现的一些简单的代码。它仅仅只是获取一组数据,再把它们都加上 2,然后再计算这些结果的和。

Failed loading gist https://gist.github.com/effab10ef3e67f2b3cfa.json: timeout

这段代码简单的跟 1 一样。但是对于那些不了解 Lisp 代码阅读的人来说就不容易消化理解了。相比于同等功能的 Scala 代码很简单了。但得让你自己下定决心。

Failed loading gist https://gist.github.com/318453e354df70c656fb.json: timeout

具有讽刺意味的是,一旦你跨过了最初的困难, s-expressions 设置了让 Clojure 成为最简单的和与现代程序设计语言保持一致的基础去使用。但是它确实需要一些努力。不可避免,但是你不得不跨过去。

放开静态类型

下一件事情会打击到有 Java 背景的你,是缺少静态类型检查(或者函数程序设计(FP)范式,如果你不熟悉 FP,我建议你学习 Clojure 之前先熟悉一下 FP,这是我的一点心得

在某些方面这是解放。毕竟,谁愿意从数据库取数据的时候还要安排数据到一个完全成熟的对象里,然后再转换成 JSON? 使用 Clojure 你就可以对这些说再见了。数据仅仅是一个 Map,也就是 JSON。嘣~,再见,没有活力的域模型。

另一方面,这有点像“哇噻! 我仅仅收到一个 map 作为参数传到我的方法!这到底是怎么做到的?”额~,你的猜测跟我差不多。

但是如果你能没有 java 的类型还能活的话,那么 Clojure 是高效的选择。

Clojure 之美

很快,你将得到更多的惊喜,而爱上 Clojure。 s-expression 基本语法的简洁,语言的连贯性,力量:这才是美。

这时你就发现你自己想站起来向 Lisp 的创造者,John McCarthy 的天才致敬,并且感谢 Rich Hickey为 JVM 创造了真正的实用主义者的 Lisp 语法。

Clojure,像 Scala,是一个实用主义者的语言,而不是纯粹主义者。它的存在是建立伟大的软件,而不是定义完美的语言。

Clojure 不做框架

一旦你着手开始使用 Clojure 构建应用,也许你会受到打击。Clojure 没有框架。它们根本不存在什么框架,一切由库组成。

在 Clojure 的世界里,方法是语言的一等公民。因此,Coljure 达到了一个彻底地超越了Java 的可组合性的新水准。这个可组合的观念部分自始至终的体现在 Clojure 的生态系统,这里框架被视作紧密耦合的绊脚石。

这在刚开始可能觉得有点令人怯步,但这绝对是一个来自的 Java 的范式转换。最初,它也需要一些跑腿的工作,就像你必须定义一些库组合到你的应用里。但是最终得到一个由最小部件组成的紧凑的应用。

另外跑腿的工作往往是值得的,你将发现有许多库都是优雅而有力的。就那 compojure-api 做个例子。这个库能够帮助你很容易就生成一个 REST API,包括支持 Swagger。

Clojure vs Scala

当年试过按范式转换的要求阅读基于 s-expressions 的语言之后,你就会觉得 Clojure 是简单的,真他妈简单。它只是列表,在列表中,在列表中 ,... ...

相比较之下,Scala 就不简单了。为什么它难呢?Clojure 是函数式程序语言(FP),像 Lisp 那样。而 Scala 是面向对象的,FP,静态类型的,等等。如果仅仅就这些原因,Scala 复杂度立马就超过 Clojure 的三倍。毫无疑问地,这是在你接触之前,还不算其他的。

不要误解我的意思,我真的喜欢 Scala。它有类型。我可以用我的 IDE 重构大量的代码库。试着用 Clojure 这样做一下,我只能祝你好运了。但是 Scala 比较复杂。由于同时具有 OO 和 FP 的模式和可变和不可变的方法,Scala 给你下了很多套。

从这点来看,Clojure 更纯净,更固执,更简单。Clojure 是简洁的。

Clojure 脚本——无名的英雄

如果你遨游在 Clojure 的世界里,迟早你将遇到 Clojure 脚本,它是 非死book React 封装。如果你以前做过可以转译成Javascript的语言和框架的Web应用开发(比如GoogleWeb Toolkit),很快,你会惊讶的连下巴都掉地上了。

在组合工具中,比如 figwheel,你可以获得实时的开发经验,figwheel 可以你使用的浏览器上最大程度的保持代码/CSS的更新。是的,你没看错,看一下这个视频 是个不错的例子。

即使你之前没有留意到,此刻,Clojure 的参数开始变得极其的引人入胜。(Scala 也有Scala.js和它自己的等同于 figwheel 的 workbench。但是,目前它在代码更新上不支持状态保留这样的水准——可能由于可能是由于静态类型的限制。

Clojure 可以开发企业级应用吗?

如果我今天打算开发一个 Clojure 项目,我知道我应该把 Clojure/Clojurescript 组合起来开发。但是我可以用 Clojure 开发一个企业级应用吗? 我不知道。

问题是企业级软件开发被一个缺乏技术性强的领导者掌控着,导致在项目中充满了反模式和离岸开发,从而引起更严重的问题。如果当公司使用一个相当简单的、静态类型的语言(比如 Java)会发生什么,哪一个相对容易的保证质量?他们用固有的权力在一个动态的,宏启用的语言(比如 Clojure)里来做什么?

对于我来说,暂时还无法提供企业级应用。是的,我还不能通过 Clojure(script) 超越以前的开发速度。毫无疑问的是,如果我用 Clojure 来开发企业级应用,我会大吃一惊。这就是我不确定的长尾理论吧。

当 Clojure 涉及到类型,Typed Clojure 就如虎添翼。一方面,它似乎存在动态类型不扩展的说明,这正是 Clojure 正在失去的一些东西。与此同时,它又提供了快速建立原型的可能性和后来增加的 Clojure 认为需要的可选类型。

我只是不能确认,以静态类型语言作为开始会不会更好,或者是否 Typed Clojure 才是是最好的中间地带。(有强烈建议的人,欢迎来说服我)

你的 FN 就是大家的 FN

Clojure 让我静下心来,并将我从 Lisp 的厌恶中解放出来。深深地印在我的脑海里。Clojure ,我向你致敬。

当然,问题是很难让每个人都用 Clojure.<o (>﹏<)>。快速浏览一下工作网站,你会发现 Scala 的工作需求量是 Clojure 的五倍。你又会发现 Java 的工作需求量是 Scala 的十倍。如果没有企业级软件社区买入 Clojure,任何事情都不会好转。这儿仍然希望有较多的实践能发掘出 Clojure 潜在的企业级应用软件开发。

如果你是 Clojure 新手,并有兴趣深入学习,并且工作前景还过的去,那么我建议你从 Light Table的 instaREPL 和一些东西像 Clojure for the Brave and True 开始。

(我在阔别 15 年后再次使用上了 Emacs ,但是这完全是另一个故事)

  1. FYI - 有很多较好的入门书籍

  2. 我可以讲更多的关于 s-expressions, Clojure, homoiconicity 等等。但是我没有。因为你可以从其他地方找到更好的。Google it.

  3. Clojure 有一些宏命令,线程优先和线程滞后,可以帮助扩展宏命令。 2

  4. Schema 和 Typed Clojure 都致力于解决这个问题,尽管它们采用了不同的方法和结果  

  5. 通过 Luminus 网页应用开始学习Clojure 是个不错的选择。它提供了“授权”设置, 让你可以将best-in-class库拖放在一起组成完整功能的应用程序