仅仅为了好玩,我们也看了看Clooj,它是用Clojure本身开发的一个轻量级的Clojure IDE.下载和运行它都很容易,不过我们发现它同Eclipse相比,就有点黯然失色了. 最后我们用了用最难的方式开发Clojure程序——只在命令行中使用Lein,还有可靠的GVIM作为文本编辑器——主要是想看看Lein是如何详细工作的. 交互式解释器(REPL)像众多的函数式语言, Clojure提供的命令行shell可以直接执行Clojure语句。这个shell对开发者非常方便,因为它在开发中不仅允许你测试一小端代码,而且允许你运行程序的一部分。 这或许对用Python,Perl开发的码农没什么新鲜的,但对Java开发者来说,这无疑带来更新鲜、更交互的方式来写代码。 函数式编程——另外一种思考方式Clojure是一种非常类似于Lisp和Scheme的函数式编程语言.函数式范式同那些习惯于Java的面向对象方式并且习惯于其副作用的方式非常不同. 函数式编程推崇:
这些特性并不是Clojure独有的,而是总体上对函数式编程都要求的: (defn send-html-response "Html response" [client-socket status title body] (let [html (str "" body "")] (send-http-response client-socket status "text/html" (.getBytes html "UTF-8")) )) 同Java的互操作性Clojure提供了优秀的同Java库的互操作功能.事实上,对于一些基本的类,Clojure并没有提供它自己的抽象,而是超乎你预期的直接使用了Java类来代替.在这个HTTP服务器的示例中,我们从Java中获取了像Reader和Writer这样的类: (ns clojure-http-server.core (:require [clojure.string]) (:import (java.net ServerSocket SocketException) (java.util Date) (java.io PrintWriter BufferedReader InputStreamReader BufferedOutputStream))) 创建和调用Java对象是非常直截了当的.而实际上有两种形式(在这个优秀的Clojure介绍中描述到了): (def calendar (new GregorianCalendar 2008 Calendar/APRIL 16)) ; April 16, 2008 (def calendar (GregorianCalendar. 2008 Calendar/APRIL 16)) Calling methods: (. calendar add Calendar/MONTH 2) (. calendar get Calendar/MONTH) ; -> 5 (.add calendar Calendar/MONTH 2) (.get calendar Calendar/MONTH) ; -> 7 下面是一个实际的样例: (defn get-reader "Create a Java reader from the input stream of the client socket" [client-socket] (new BufferedReader (new InputStreamReader (.getInputStream client- socket)))) 然而,对于一些结构,我们决定要使用到Clojure的方式.原生的Java代码使用StringTokenizer,这样做违背了不可变对象的纯函数式 原则,还有无副作用的原则.调用nextToken()方法不仅有副作用(因为它修改了Tonkenize对象)并且使用同一个(或许是不存在的)参数也 会有不同的返回结果. 由于这个原因,我们使用Clojure的更加"函数式"的Split函数: (defn process-request "Parse the HTTP request and decide what to do" [client-socket] (let [reader (get-reader client-socket) first-line (.readLine reader) tokens (clojure.string/split first-line #"\s+")] (let [http-method (clojure.string/upper-case (get tokens 0 "unknown"))] (if (or (= http-method "GET") (= http-method "HEAD")) (let [file-requested-name (get tokens 1 "not-existing") [...] 并发(Concurrency)Clojure从一开始设计对并发很上心,而不是事后诸葛亮.使用Clojure编写多线程应用程序非常简单,因为所有的函数默认都实现了来自Java的Runnable和Callable接口,自身得以允许其任何方法在一个不同的线程中运行。 Clojure也提供了其它特别为并发而准备的结构,比如原子(atom)和代理(agent),但是我们在这个HTTP服务器示例中并没有使用它们,而是选择熟悉的Java的Thread. (defn new-worker "Spawn a new thread" [client-socket] (.start (new Thread (fn [] (respond-to-client client-socket))))) 有关方法使用顺序的问题我们认识到的一件事情是在源代码文件中,方法的顺序是严格的。函数必须在它们第一次被使用之前被定义。另外一种选择是,你可以在一个函数被实际定义之前利用一种特殊的声明格式,来使用函数。这让我们想起了C/C++的运作方式,它们使用头文件和函数声明。 原文链接:http://zeroturnaround.com/rebellabs/the-adventurous-developers-guide-to-jvm-languages/ 译文链接:http://www.oschina.net/translate/the-adventurous-developers-guide-to-jvm-languages |