探索JVM上的LISP

当前Java领域最激动人心的事情莫过于可允许其它编程语言运行于Java虚拟机上。围绕JRuby、Groovy、Scala还有 Rhino(JavaScript引擎)的讨论已经甚嚣尘上。可为什么要墨守陈规呢?如果你真的想跳出主流,投身于一种与Java截然不同的的语言,Lisp就不失为一种很好的选择。现在已有几种可运行于JVM上的Lisp程序设计语言的开源实现,准备好开始我们的探索之旅吧!

Lisp有什么值得研究呢?首先,作为已有50年历史的语言,它促成许多被我们今日视为理所当然的观念。if-then-else结构、早期的面向对象和带垃圾回收的自动内存管理的尝试都来源于此。目前Java程序员的热点话题——词汇闭包(Lexical Closure),最初的探索也是七十年代在Lisp中展开的。除此以外,Lisp还具备其它许多语言至今都未采用的特性,这些出色的思想必将在未来引起复兴潮流。

本文的目标读者是有意了解Lisp的Java开发人员。我们将在接下来的内容中讨论当前可以用在JVM上的不同Lisp方言(dialect),令你快速了解Lisp程序设计工作机理和其独特之处,文章的最后会演示如何将Lisp代码与Java系统进行整合。

目前存在许多可用于不同平台的Lisp系统,有免费的也有商业的。对于想要开始探索Lisp的Java用户,不离开JVM是首选,这样的话起步很容易,还可以很方便的使用所有自己熟悉的Java库和相关工具。

Common Lisp和Scheme

Lisp有两种主要方言(dialect):Common Lisp和Scheme。虽然设计理念大体相似,但是它们的差别仍然足够引起孰优孰劣的激烈争论。

Common Lisp是1991年完成的ANSI标准。统一了几种早期Lisp的理念,是可用于多种应用开发的大型环境,其最为著名的应用是人工智能。而Scheme 产生于学术界,特意进行了精简化设计,经验证是一种很好的语言,既可用于计算机科学教学,又可以作为嵌入式脚本语言。你还可能会遇到其它一些比较有名的 Lisp:小型的特定于应用的DSLs,如Emacs Lisp或AutoCAD的AutoLISP。

上面提到的两种主要方言(dialect)在JVM上都有相应的实现,相较而言Schemes的实现要成熟一些。Armed Bear Common Lisp(www.armedbear.org/abcl.html)非常彻底的实现了Common Lisp标准,但它存在一个问题,如果你没有安装别的Common List系统,就不能构建分发版本,这对新手可能是个困难。

在Scheme方面,两个主要的产品是Kawa(www.gnu.org/software/kawa)和SISC(www.sisc-scheme.org——the Second Interpreter of Scheme Code)。在这篇文章的例子当中,我们会用到Kawa,它实际上是个框架,能创造可编译成Java字节码的新语言。Scheme只是它的实现之一。顺便说一句,Kawa的创建者Per Bothner目前就职于Sun,主要从事JavaFX项目的编译器方面的工作。

另外一个值得一提的竟争对手是Clojure(clojure.sourceforge.net)。这是一种新的语言,其Lisp方言(dialect)介于Scheme和Common Lisp之间。它是直接为JVM量身打造的,因此在上面提到的所有Lisp当中,有着最为清晰Java整合方案。它还具有其它一些激动人心的特性,例如内建的支持并发和事务内存。Clojure目前仍然处于探索测试阶段,因此在它基础上构建程序还有些为时尚早,但它绝对是一个值得关注的项目。

读取—求值—打印—循环

我们先来安装Kawa。它的分发版是一个单独的Jar文件,可以直接通过链接ftp://ftp.gnu.org/pub/gnu/kawa/kawa-1.9.1.jar下载。得到该Jar包后,就把它加进你的类路径上,这样你就可以通过运行如下命令启动REPL了:

java kawa.repl
  #|kawa:1|#

该命令启动了Kawa,并显示一个提示符。这其中究竟有何奥妙呢?REPL(READ-EVAL-PRINT-LOOP)意思是读取—求值—打印—循环,这是与运行中的Lisp系统进行交互的方式——它“读取”你的输入,进行“求值”运算后,“打印”计算结果,如此反复“循环”。开发Lisp程序的方式,与我们开发Java程序时所遵循的“写代码、编译、运行”的周期不同。Lisp程序员需要激励他们的Lisp系统,保持它的运行状态,这样就令编译和运行时的界限模糊起来。在REPL中,函数和变量在执行过程中都是可以修改的,代码也是动态解释和编译的。

先来做点简单的事情:把两个数字加到一起。

#|kawa:1|# (+ 1 2)
  3

这是Lisp表达式的典型结构或者说“格式”。语法都是一致的:表达式总被放在一对圆括号内,因为用的是前缀符号,所以“+”号要放在两个参量前。再来一个复杂点的结构,把几个格式嵌套在一起,建立一个树状结构:

#|kawa:2|# (* (+ 1 2) (- 3 4))
  -3

Scheme的内建函数以同种机理工作:

#|kawa:3|# (if (> (string-length "Hello world") 5)
          (display "Longer than 5 characters"))
  Longer than 5 characters

上面程序中,用一个if语句来检查某一特定字符串的长度是否超过5个字符,如果像例子中的那样检查结果为真,就会执行紧随其后的表达式,该语句将会打印一条提示信息。注意这里的缩进只是为了增加可读性,如果你愿意的话,可以在一行内写下所有的语句。

Lisp代码用的这种括号密集(parenthesis-heavy)的风格也称为“S表达式(s-expressions)”。它可兼作定义结构化数据的通用方法,就像XML一样。Lisp有很多内建的函数,你可以很方便的应用S表达式格式操纵数据,这种便利转而促成Lisp的另外一个强大优势:既然语法是如此简单,那么编写产生、修改代码的程序也要比其它语言简单得多。当我们演示宏(macros)的例子时,会了解到更多类似情况。

时间: 2024-09-23 05:49:54

探索JVM上的LISP的相关文章

Clojure快餐教程(1) - 运行在JVM上的Lisp方言

Clojure快餐教程(1) - 运行在JVM上的Lisp方言 Java作为目前为止被使用最广泛的使用虚拟机的编程语言,带动了JVM上语言族的繁荣. 有根红苗正的为JVM设计的动态语言Groovy,目前最主要被用于Gradle编译环境中:也有Jython, JRuby等动态语言在JVM上的实现,也有scala这样强大的混合语言. 在这之中,clojure是比较特殊的一种,它是Lisp语言在JVM上的一种方言. 使用clojure调用java 首先我们先看一下如何用clojure来调用java的方

JVM上的随机数与熵池策略

在apache-tomcat官方文档:如何让tomcat启动更快 里面提到了一些启动时的优化项,其中一项是关于随机数生成时,采用的"熵源"(entropy source)的策略. 他提到tomcat7的session id的生成主要通过java.security.SecureRandom生成随机数来实现,随机数算法使用的是"SHA1PRNG" private String secureRandomAlgorithm = "SHA1PRNG"; 在

Adopt Open JDK官方文档(十)

编译过程性能优化的命令行参数 AdoptOpenJDK wiki的一个链接,提供了几个怎么提高编译过程性能的例子. 编译 jcov 项目首页(项目信息, 编译指南, 其他-) https://wiki.openjdk.java.net/display/CodeTools/jcov 源代码: http://hg.openjdk.java.net/code-tools/jcov 从Adopt OpenJDK持续集成网站下载.   快速编译指南 $ hg clone http://hg.openjdk

《Scala程序设计》阅读书摘

JVM语言 JVM上的语言越来越多了,从前几年的groovy.Scala和Clojure,现在又听说一门Kotlin.对于前三种语言,groovy算是JVM平台上的动态脚本语言,可以类比Python:Scala以其直逼C++的语言复杂度而出名,但是该语言的流行是因为高并发(akka)和大数据处理(Spark):Clojure是JVM上的Lisp重现,是纯粹的函数式编程语言,它在实时计算(Strom)中承担了一部分责任. 选择Scala 我决定了解Scala的原因是高并发,以及它与Java具备良好

Java.next:下一代的JVM语言

本文是ThoughtWorks公司架构师Neal Ford在IBM developerWorks系列文章java.next中的第一篇,其基于Groovy,Scala和Clojure,讲述了多语言编程的重要性,并对静态类型与动态类型,函数式编程与命令式编程进行了比较.(2013.02.06最后更新) 在我与Martin Fowler曾经合作呈现的一次主题演讲中,他作出了一个有洞察性的观点: Java的遗产将是平台,而不是程序设计语言. Java技术的原始工程师们作出了一个明智的决定,就是将编程语言

JVM性能优化(三):垃圾收集

原文地址,译文地址,译者:Greenster Java平台的垃圾收集机制显著提高了开发者的效率,但是一个实现糟糕的垃圾收集器可能过多地消耗应用程序的资源.在Java虚拟机性能优化系列的第三部分,Eva Andreasson向Java初学者介绍了Java平台的内存模型和垃圾收集机制.她解释了为什么碎片化(而不是垃圾收集)是Java应用程序性能的主要问题所在,以及为什么分代垃圾收集和压缩是目前处理Java应用程序碎片化的主要办法(但不是最有新意的). 垃圾收集(GC)的目的是释放那些不再被任何活动对

jvm系列(七):jvm调优-工具篇

16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗费性能的分析(dump文件分析)一般也不会在生产直接分析,往往dump下来的文件达1G左右,人工分析效率较低,因此利用工具来分析jvm相关问题,长长可以到达事半功倍的效果来. jvm监控分析工具一般分为两类,一种是jdk自带的工具,一种是第三方的分析工具.jdk自带工具一般在jdk bin目录下面,

JVM之上的语言小集

1 JVM上的编程语言https://en.wikipedia.org/wiki/List_of_JVM_languages主要的有:Clojure, a functional Lisp dialectGroovy, a programming and scripting languageScala, an object-oriented and functional programming language[1]JRuby, an implementation of RubyJython, a

JVM内存管理:GC算法精解(五分钟让你彻底明白标记/清除算法)

首先,我们回想一下上一章提到的根搜索算法,它可以解决我们应该回收哪些对象的问题,但是它显然还不能承担垃圾搜集的重任,因为我们在程序(程序也就是指我们运行在JVM上的JAVA程序)运行期间如果想进行垃圾回收,就必须让GC线程与程序当中的线程互相配合,才能在不影响程序运行的前提下,顺利的将垃圾进行回收. 为了达到这个目的,标记/清除算法就应运而生了.它的做法是当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被成为stop the world),然后进行两项工