Chap-8. scheme 処理系の比較(スクリプト使用を前提)


Sec-1. 処理系の選定

 ddns-tool の様なスクリプトを作るとき、どれが使いやすいか、比較してみたい。そこで、処理系をいくつか選定します。
* まずはSCM です。なぜ最初にこれを選ぶかというと、実はscheme をやりだした最初のうち、私にはこれくらいしか使えなかったのです。始めてLisp 系言語に興味を持ったのは1992から1993年頃でした。その頃はネットを利用してソースを持ってくることは出来ませんでした。そこでPrime Time Freeware for UNIX というCD-ROM2枚セットの本を買いました。これには当時としては大量のフリーウェアソフトのソースが入っていました。当時使っていたUNIX機は幾分マイナーなもので、CPU が、X86やSparc ではありませんでした。このため、フリーソフトウェアをコンパイルしてインストールするのに幾分手間取りました。Lisp は、最初は全部ダメでした。なんとかコンパイルしてインストール出来たのはscheme 処理系のうち、SCM とElk の2つでした。少し時間をかけると、その後でKyoto-Common-Lisp を動かすことができました。でもその時はscheme を学び始めたところだったので、結局Lisp は全然やりませんでした。そういうわけで、私が最初に触れたLisp 系の言語はscheme で、そのうちの処理系としてはSCM とElk だったわけです。Elk はもう精力的なメンテがされていないようなので、除外します。
* 次に、gauche、 guile、 racket、chicken を選びます。選ぶ理由は、一つにFreeBSDの package に入っていること。もう一つは、ネット上で「scheme 処理系、おすすめ」を検索してみて、決めました。人気があるものや、srfi が使いやすいものです。


Sec-2.比較する関数

 とりあえず比較する関数は、スクリプト処理をするときによく使いそうな関数、基本的に以下のとおりとする。


Sec-3.比較結果の一覧表

 一覧表にすると、おおむね以下の様になりました。

this is scheme comparison

注1.(require 'i/o-extensions) とすれば、使える

注2.スクリプトを実行すると以下の様になる

#!/usr/local/bin/scm
(print *argv*)
(exit)

これをtest.scm として実行可とする。
$ test.scm aa bb cc
とすると
$ (/usr/local/bin/scm  test.scm  aa  bb cc)
と出力する。(リストの中は文字列)

注3.(use file.util) とすれば、使える

注4.他の処理系と同様に使える。(define system sys-system) とでも最初に記述しておけば、同じソースが使える。

注5.スクリプトを実行すると以下の様になる

#!/usr/local/bin/gosh
(print *argv*)
(exit)

これをtest.scm として実行可とする。
$ test.scm aa bb cc
とすると
$ (aa  bb cc)
と出力する。(リストの中は文字列)

注6.(use srfi-13) とすれば、使える

注7.スクリプトを実行すると以下の様になる

#!/usr/local/bin/guile -s
!#
(print (command-line))
(exit)

これをtest.scm として実行可とする。
$ test.scm aa bb cc
とすると
$ (test.scm aa  bb cc)
と出力する。(リストの中は文字列)

注8.(use-modules (ice-9 rdelim)) とすれば、使える

注9.スクリプトを実行すると以下の様になる

#!/usr/local/bin/racket
#lang racket
(print (current-command-line-arguments))
(exit)

これをtest.scm として実行可とする。
$ test.scm aa bb cc
とすると
$ #(aa  bb cc)
と出力する。すなわち、racket だけは引数をベクターで保存している。(ベクターの中は文字列)

注10.(require srfi/13) とすれば、使える

注11.(use utils) とすれば使える。

注12.スクリプトを実行すると以下の様になる

#!/usr/local/bin/csi -script
(print (argv))
(exit)

これを test.scm として実行可とする。
$ test.scm aa bb cc
とすると
$ (/usr/local/bin/csi -script test.scm aa bb cc)
と出力する。

注13.(use srfi-13) とすれば使える。


 補足1.gauche はマルチバイト文字を扱えるのが売りである。ただし、私のようなスクリプトの使い方だと、やや具合が悪い。
ルーターから読み出したファイルを1行ずつたどって、目的のアスキー文字列を含んだ行を探す場合、その行は「単なるバイトストリーム」として扱われることが望ましい。しかしマルチバイト文字(例えばutf-8)対応でコンパイルされた gauche の場合、アスキー文字と utf-8 はうまく扱ってくれるが、この行の中にアスキーとutf-8 以外の文字、例えば Shift-JIS 文字が混じっているとそこでエラーを出して停止してしまう。これは、厳密にマルチバイト文字を扱う場合正しい処理だが、私のスクリプトでは具合が悪い。探すのはアスキー文字で、それ以外は非表示バイトだろうが、Shift-JIS だろうが utf-8 だろうが気にせずに、バイトストリームとして処理をしてもらいたい。
結局、このエラーを回避するために私がとった手法は gauche をマルチバイト非対応としてコンパイルしなおすことだった。

 補足2.racket は if クローズを [ if condition then-exp else-exp ] として処理する。この場合 racket では else の部分が必須である。
他の処理系だと、else はオプショナルなので、racket だけが、ここが全然違う!
これが、RnRS か何かの仕様変更なのかどうか、まだ調べていない。興味のある方はじっくり調べてみてください




Sec-4. ごく個人的な結論

 以上の比較を踏まえて、BL900HW ルータを使ったddns-tool をSCM 以外にgauche、guile、racket、chicken で動かしてみた。どの処理系でも、特に問題なく動かすことが出来た。
上記の比較を見れば、それぞれの好みでどの処理系が望ましいか決まってくると思う。ようは好み次第である。
 私の場合、この比較当初は SCM を使い続けるつもりでした。しかしながら後になって、とあるプログラム実行中にミリセコンドの時間計測をしようとしたとき、SCM ではこれが実現しづらいことが分かった。このため、SCM を使い続けることはあきらめた。一方、racket は if 文にクセがある。コマンドラインをベクターとして扱うのも、私が調べた中では racket だけだ。また、スクリプト中で他のファイルをロードする方法が、他の処理系と異なっているようだ。私は racket だけ、スクリプト中でうまくファイルがロード出来なかった。racket を使おうとすると、ドキュメントをもっとしっかり読まなくてはならないだろう。それで racket も使うことを諦めた。
 結局、guile、gauche、chicken の三者が残った。三者いずれも機能は充実しているし、ドキュメントも内容が豊富という意味で充実している。いずれも私にとっては申し分のない処理系と言える。なおかつ、この三者の間での優劣は私の比較からは判別できない。そこで、当面は guile を使うことにした。たとえ有能な方とはいえ個人でサポートされている gauche には、サポートの継続性にやや不安がある。chicken はコミュニティでサポートされていて、その点では安心だ。しかし2015年時点では、まだコミュニティ内部に混乱がありそうだ。そう考えると、GNU でサポートされている guile がサポートの継続性や安定性という点で、一番信頼できそうに思えた。
もっともこんな結論は、極めて個人的で、いいーー加減な結論です。そのうち全然違うことを言っているかもしれない。(笑)


 2016年9月13日修正:
 if 文の中の else 部分が必須になったのはやはりR6RS からです。
また、R6RS からはload 関数がなくなったらしいです。多分、racketは、忠実にR6RSにしたがっているのでしょう。
しかし、なんなんでしょう?このscheme改訂は。
今まで使えていた文法を急に変えたり、使えていた(便利だった)関数を廃止したら、今まで動いていたプログラムが突然動かなくなってしまいます。一体Scheme の改訂って何のためにやっているのか、理解し難いものがあります。