你可以通过 ClojureScript 使用 Clojure 语言编写代码,然后将其编译为 Javascript。ClojureScript 是由 Clojure 的创建者 Rich Hickey 发布的(这里是 Rich 的 ClojureScript 声明及 ClojureScript 声明的视频)。
ClojureScript 是 Clojure 的一个子集,目前缺失的特性与库要么是尚未实现,要么是由于对 Javascript VM 毫无意义而不会添加进来,比如线程支持、Java 集成等等。ClojureScript Wiki 上详细列出了Clojure 与ClojureScript 之间的差异列表。
设计ClojureScript 的缘由
ClojureScript 的基本原理(摘录自文档)就是“做 Javascript 所能做到的事情”。Javascript VM 正变得越来越快,应用的领域也越来越广。借助于 ClojureScript,我们可以将 Clojure 用于 GUI 客户端编程、包括移动平台(带有强大的 HTML 组件与 Javascript VM)。运行 Clojure 需要客户端安装有 Java VM;虽然桌面操作系统可能都有 Java VM,但浏览器的应用却更加普遍,并且每个客户端操作系统都会有浏览器。这种局面在移动平台上就更清晰了——目前还没有哪个主流的移动平台带有可以直接运行 Clojure 的 Java VM。Android 并不带 Java VM;它需要将所有字节码转换为 Dalvik VM 字节码;现在人们还在为能让Clojure 顺利运行在Android 上而不断努力。
ClojureScript 还有另一个使用场景:命令行程序。Java 的命令行程序使用的并不多,特别是需要频繁加载的程序,原因在于 JVM 的启动时间(诸如Nailgun 之类的解决方案就是为了解决这个问题而出现的)。正如Rich Hickey 所述,如果将命令行程序编译为Javascript,然后使用Node.js 运行会大大降低启动时间。
关于在服务器端使用Javascript 的一个普遍观点是可以在服务器端和客户端重用相同的代码;特别是像输入验证这样的逻辑。借助于ClojureScript 依然可以做到这一点:使用ClojureScript 编写的算法可以编译为Javascript 并运行在浏览器中,同时还可以用在服务器端的Clojure 代码基中,这时算法会被编译为Java 字节码。它还为在服务器端完全抛弃Java 提供了一种可能——使用ClojureScript 编写应用,然后在Javascript 栈如Node.js 上运行服务器端。
ClojureScript 的实现方式
ClojureScript 编译器并没有任何 Javascript 代码,它使用 Clojure 编写,这意味着编译器需要运行在 Java VM 上。因此,ClojureScript 缺少 eval 和其他类型的运行时代码加载能力。ClojureScript 的目的在于编写程序并将其编译为 Javascript 而非作为浏览器中的 REPL。
然而,确实有 ClojureScript REPL,它位于 ClojureScript 仓库中,使用 Clojure 实现,它用于启动 Rhino——使用 Java 编写的 Javascript 运行时。要想运行 ClojureScript 代码,需要将其发送给基于 Clojure 的 ClojureScript 编译器,后者会返回 Javascript 代码,然后由 Rhino 运行。在浏览器中不通过 Clojure 实例来这么做显然是不可行的——至少需要将 Clojure 编译器工具链编译成 Javascript 才行。
ClojureScript 代码可以使用 Clojure 宏。宏是个编译期的特性;如果 ClojureScript 代码引用了宏调用,那么宏扩展就会被 ClojureScript 编译器所执行,在 Clojure 中执行。
虽然目前 ClojureScript 编译器只能运行在 Clojure 上,但 ClojureScript 却带有 Clojure Reader。说明一下,Reader 基本上是个 Clojure 解析器;它会将文本形式的 Clojure 程序转换为 Clojure 数据结构,然后再对该数据结构求值。ClojureScript 自带的 Reader 可以解析 Clojure(Script)符号中的数据,然后以数据的形式将其传递给 ClojureScript 代码。ClojureScript 的 Reader 只会解析,由于运行期并没有 ClojureScript 编译器,因此它并不会求值。
ClojureScript 之所以带有 Reader 的目的在于读取 Clojure 数据,就像 Javascript 无需求值就可以读取 JSON 数据一样。我们可以通过 Clojure 生成 Clojure 数据并将其发送给 ClojureScript,反之亦然。
事实上,ClojureScript 编译器工具链的另一部分是 Google Closure 工具集。没错,带有字母“s”的 Closure,他们的命名很像,但 Google 的 Javascript 工具 Closure 集合,特别是 Closure 编译器和 Advanced Compilation 都用到了。
Closure 可用于几个目的:其中一个目的就是库与依赖的管理,这需要库与导出符号。ClojureScript 命名空间定义与 Google Closure 的 provide 与 require 调用对应。这样,ClojureScript 就可以轻松使用 Google Closure 库了,该库拥有非常丰富的 GUI 组件和其他特性。
ClojureScript 与 Clojure 语言非常像,但其标准库是 Clojure 的一个子集。目前,ClojureScript 的标准库有 clojure.string、clojure.set、clojure.walk、clojure.zip 等,同时更多的库将会被添加进来。将 Clojure 库移植到 ClojureScript 的难度取决于代码本身;仅使用基本语言元素来转换数据的纯算法代码很容易移植。使用了 I/O 库、线程、特定 Java 库的库则需要更多的工作;我们还需要分解平台特定的代码,这都是必要的工作。
Google Closure 一个有趣的用法就是作为优化编译器的后端。ClojureScript 编译器所生成的代码会进行优化以便与 Closure 的 Advanced Compilation 协同工作,它会接收 Javascript 源代码并对其进行优化,包括内联的函数调用、删除死代码(dead code)与无用的函数。这种方式的一个优势在于 ClojureScript 编译器无需实现这些优化工作了;它只需将其委托给 Google Closure 即可。
未来,其他的工作也可以委托给 Google Closure,比如为调试器提供源代码图,也就是说数据结构可以匹配生成的 Javascript 代码和生成它的 ClojureScript 代码。 Google Closure 对 SourceMaps 提供了一定的支持,同时 Mozilla 与 WebKit 项目都在致力于扩展其浏览器、调试器和 Javascript 引擎来支持他们。
不久大家就会看到关于 ClojureScript 的更多信息;其中一个信息来源就是 Michael Fogus 的博客,他已经发布了关于编译器系列文章的第一部分。Michael 从事于ClojureScript 实现。对编译器内部机制感兴趣的开发者们还应该看看 ClojureScript 开发说明,上面列出了很多信息,比如 ClojureScript 语言结构及其转换而成的 Javascript 结构。
Clojure 社区已经开始尝试 ClojureScript 了。Brian McKenna已经开始尝试使用宏来与回调地狱(callback hell)展开斗争,这指的是异步I/O 的每个操作都需要提供一个回调来接收操作的结果——这么做一两次还行,但对于连续的算法来说实在过于冗长了。在JS 世界中有不少解决方案在尝试解决这个问题,从新的语言如 StratifiedJS 到使用Javascript 编写的嵌入式DSL 库。
Brian 混合使用了嵌入式 DSL 解决方案与 ClojureScript,就像是带有宏的 LISP 一样,效果不错。试验处理了一系列的表达式并将其编译为嵌套的回调,每个回调都在序列中执行一个表达式,然后通过 setTimeout 调度序列中其他回调的执行。这不禁让人想起了支持 Monads(Haskell 的 do 符号)的语言或是 F#的 Compuational Expressions( Async Workflows )的程序分号方式。接下来将会支持更多的结构化程序设计概念,如循环,使用宏将其转换为这种连续的传递形式。
Justin Grant 的一篇博文介绍了如何实现一个算法并使用Google Closure 的Canvas 支持来绘制图像。
ClojureScript 位于 GitHub。要想了解更多信息,请查看clojure.com 上的ClojureScript 声明,或是 GitHub 上的 ClojureScript Wiki ,上面有大量的信息,包括 ClojureScript 的基本原理、快速起步指南等内容。 Clojure 邮件列表也是个不错的信息来源,你可以从中了解到 ClojureScript 的使用方式,社区也在上面讨论开发工作流、工具等信息。
查看英文原文: ClojureScript Brings Clojure To The Browser via Javascript
评论