Lizpop介绍

简介

实现一个Lisp方言实在是太简单了,尤其是作为方言之一的Scheme,所以各个语言版本的Lisp实现层出不穷,Python平台上也是如此。最著名的当属Peter Norvig的lis.py

其中最靠谱的当属clojurepy,直接将源代码编译为Python字节码执行,无论是速度还是与Python库的互通性都是无与伦比。不过Scheme就没这么靠谱的实现了,大部分都是个简单的Demo,或者没办法调用Python代码,或者很多特性没实现。

Lizpop相对不错一些,不过没有官网和文档,只有个简单的README,并且到目前位置代码已经一年没有更新了。看名字作者是个日本人,不知道以后会不会维护下去。

安装与运行

用pip的同学就简单了,直接pip install lizpop即可。

不过lizpop并没有提供可执行文件,需要用python -m lizpop.run来运行。不带参数直接进入REPL,带上文件名的话会执行指定文件。文档的例子是给python解释器加-O的参数,这样会对python字节码做优化,可能对于lizpop这种应用场景来说优化余地比较大吧。

使用

了解Scheme的同学自然知道,用不着我多说。

类似Python,lizpop有个叫help的procedure,可以看对象的code doc,比如:

> (help load)
Procedure: (load FILE-PATH)
  Load program from FILE-PATH.
  If FILE-PATH doesn't absolute-path and doesn't start with
  "./" or "../", it is searched through the directories on
  *load-path* variable

  See Also: (help *load-path*)

#<none>

引入其他的scheme代码文件用上面的load即可,但是要引入python代码,就得用import了,比如(import "this")。不过import后只是把模块的引用返回回来了,需要自己手动保存到变量中。

获取Python对象的属性可以用attr,这样和Python互通就没有大问题了,可以看下面的例子:

$ cat helloworld.py
def hello(s="world"):
  return u"Hello %s!" % s.capitalize()

$ python -O -m lizpop.run
> (define *helloworld* (import "helloworld"))
> (define hello (attr *helloworld* 'hello))
> (hello "friends")
"Hello Friends!"

lizpop里面很多类型就是python原生类型,这点很赞。并且字符串都是python unicode对象。内置过程,也部分是python function对象(或者实现了__call__的对象),但用户自定义的过程和一小部分内置过程是lizpop内部实现的Closure类的对象。此对象并没有实现__call__方法,因此有不少麻烦,比如在scheme里给python传递回掉函数的时候。

这个时候可以用@callable来将Closure对象封装成python function。

另外还可以通过builtin来访问python的__builtin__invoke可以直接通过对象的引用和属性名来直接调用对象的方法(这点没clojure那样的语法支持,写起代码来会痛苦很多)。

EML

EML是EMbedded Lizpop的缩写,这个是作者实现的一个模板库,直接可以嵌入scheme代码,用起来大约是这样:

$ cat gcdlcm.eml
<% (define numlist (map string->number *args*)) %>
GCD of <% numlist %> is <%(apply gcd numlist)%>.
LCM of <% numlist %> is <%(apply lcm numlist)%>.

$ python -m lizpop.run -eml gcdlcm.eml -- 1533 37303 4307
GCD of (1533 37303 4307) is 73.
LCM of (1533 37303 4307) is 6602631.

不足

除了上面所说,文档还列出了几点:

  • 不支持卫生宏(不过还是可以用传统宏的)。
  • 因为字符串其实就是python unicode对象,是不可变的,string-set!一类的自然就不能用了。
  • call/cc不完善。
  • 不支持复数与分数。
  • null-environmentscheme-report-environment没有实现。

例子

最好的代码就是内置的标准库boot.scm,在包里可以找到。另外尝试了一下调用bottle写web应用,感觉还不错:

#!scheme
(define bt (import "bottle"))
(define route (attr bt "route"))

(define (index) "Hello")

((route "/")
  (@callable (lambda ()
               "Hello Lizpop!")))

(invoke bt "run")