Chap-8. scheme 処理系の比較



Sec-1.前書き
Sec-2.比較する処理系
Sec-3.比較結果
Sec-4.速度の比較(簡易比較)



Sec-1. 前書き

 2014-10 に初稿を書きました。その時はファイル操作の関数などを比較しました。ただし結論としては「そういう比較をしても処理系の優劣は判断出来ない」というものでした、汗。
 2025-03: 今回は、少し趣きを変えて全般網羅的にコメント・感想を羅列しました。タイトルは「比較」ですが、「単なる私の感想」です。




Sec-2.比較する処理系

 比較する処理系(望ましい処理系)は

としました。

組み込みについては chap-15. で触れています。昨今の自分の気持ちとしては「組み込み」だけでなく「組み込みライクなやり方」でも良いかなあ・・・なんて思っています。が、ここでは取りあえず「組み込み」だけに限定しました。



 人気や評判については、人気投票ウェブサイトを参考にしつつ選びました。マイナー言語の scheme で人気投票を開催する人がいる事に驚きました。世の中は広いです、笑。
人気投票サイトは下記のとおり。

タイトル
81 Best scheme implementations as of 2024 - Slant
URL
https://www.slant.co/topics/5282/~scheme-implementations

結果的に、選んだのは chicken、guile、chibi-scheme でした。選んだ理由等を下記で説明します。


訂正:このチャプターを書きなおした後で判明しましたが、chibi は一部(string-contains)で古い仕様を捨てているようです (srfi-13 でなく srfi-130 を採用)。25とか30年くらい前は srfi-13 が標準だったと思います。なので、その部分では古いプログラムが動きません。イマイチです。
古いプログラムを走らせる場合、chibi だと string-contains は自作する必要があります、笑。そんなに難しく無いですが・・・





Sec-3.比較結果 (結果というより個人の感想です)

 参考になる URL を以下に示します。

タイトル
人気の言語を作るには  
URL
http://practical-scheme.net/trans/being-popular-j.html

Gauche の作者さんが翻訳したものが同サイトに掲載されています。とても面白いし参考になります。以下で、ちょっとだけ引用します。
このような素晴らしい文書を翻訳なさった Gauche 作者さんに感謝です。






第一に、大事な点・・安定性とか堅牢性とか
 正確に動くとか、バグが少ないとか、安定しているとか、一番重要な部分です。
まず、人気と継続性、サポート規模等: 
 guile、chicken は処理系として長い歴史があります。サポートしているところも、大きなところです。なのでノー天気な発想ですが、人気から判断すれば大丈夫です。
chibi についてはサポート団体の正体がよく分かりません。大きいのか小さいのかも分かりません。ただし 2009 年頃には既に R7準拠等の関係で chibi を勧めている話がネット上にあります。つまり最低でも15年以上の実績があります。またコントリビュータの数が70人近くいるようです。そんなことなので取りあえず chibi もオケと考えました。人気の面やサポート等から見れば三者ともオケです。
使ってみての安定性
 ただし chicken のコンパイラがやや微妙です。cond 文の中で
((symbol? data) (set! data (symbol->string data)))
とやったのですが、chicken のコンパイラーがこの部分でワーニングを出してきます。「symbol->string は symbol に対して適用するはずなのに、data に適用していて、これはまずいぞ」という警告です。そもそも symbol かどうか判定した上で symbol->string しているのだから、何も間違っていない。ここで data はただの変数です。他の処理系でここで警告やエラーを出すものはありません。どうも chicken のコンパイラーの一部に「バグ一歩手前」があるようです。(chicken のインタープリタはこの部分を問題なく実行できます)
10 年ほど前にネット上で「chicken はインタープリターを作っているチームと、コンパイラーを作っているチームが別らしい。両者の挙動が違っていて具合が悪い」という書き込みを見た記憶があります。今回はそれを微妙に実感しました。ただし今回のは問題ではなく、問題の一歩か二歩手前くらいです。
そもそも、出しているのはワーニングだけ。コンパイル結果のプログラムは問題なく正常に動きます。さっきから繰り返し「微妙」と書いているのはそういう意味です。
なので判断としては、ギリギリセーフ。(基本的にオケ)




第二に、FFI、embedding いわゆる組み込みに対応しているか
 3つとも対応しています。C言語に組み込み出来ます。やり方(サンプル)を chap-15.で示しています。個人的に、この項目は実用性が高いと思います。
実は本音を言いますと、なんとか FFI(embedding)が使いこなせそうだったのがこの3つなのです、笑。他の処理系は難しくて使いこなせません。



第三に、新仕様に適切に追随しているか
 以前のことですが、きっちり R6 に準拠した処理系(例えば Racket )は R5 時代に組んだプログラムのかなりが動かなくなりました。
R6 は if クロースの else 部分を必須にしたのですが、もともと R5 では任意だったのです。そんな具合で、古い機能を簡単に捨てちゃう処理系は、後になって困りそうです。なので上に書いたように「R5 ベースにして R6 R7 等を追加する処理系」にしたわけです。
この3つは基本的に R5 に新機能を追加し続けているので、この先も多分大丈夫ですが、chibi の書式の制限には個人的不満があります。
今回久しぶりに複数の処理系を試しました。それで気がついたのすが最近の大半の処理系は書き方に注文が厳しいようです。一種の書式制限。
以前は任意に define define define って感じで書いていました。30 年くらい前の scheme の書物にも「scheme では set! はあまり使わない。ガベージコレクションが適切に働くので define をメインに使えば良い」という解説でした。
ところが最近の処理系は先に (let ((hogehoge )) で変数等の領域を確保して、後の方でそれを set! するようです。そういう書き方をしないとエラーで弾かれてしまいます。何とも、変な仕様。
define を多用するのはむしろ scheme 本来の使い方だったはずです。それが今になってエラーで弾くって、どうかしています。文法上間違っていなかったものを、後になってから否定する仕様は良くないと思います。
残念ながら chibi を始めとして多くの処理系がその仕様になっているようです。がっかりしました。
もしかしたら未だに「30年くらい前のプログラムでも動かす。書き方の仕様変更はやらない(R6 の指定には従わない)」という方針なのは chicken guile の2つだけなのかもしれません。



第四に、速度
 これは思っていたよりずっと良い結果でした。
比較するための簡易テストは Sec-4. に示します。簡易テストなのでこの結果が絶対的真理ってわけじゃないです。
一応、結果から言うと

というものでした。余談ですが chibi の速度だと http サーバに組み込んで使うのは無理だと思います。遅すぎ。





以降、その他の項目です。

書き捨てプログラム用の言語(又はスクリプトで使われる言語)
 上記参考ウェブサイトの記事中で多数ある面白い部分の一つとして「書き捨てプログラムに使いやすい言語が、一つの重要な条件」というのがあります(スクリプトで使われる言語という話も文章中にあり、似た話です)。名言だと思います。
宅サバ・宅UNIX・宅プログラミングの土俵で考えれば、言語の実用的な利用分野の大半は書き捨てプログラムでしょう。5行とか20行とか、もうちょい多いくらいのプログラム(「スクリプト」というのはこういうものを指す言葉らしいです)
ちょっとした処理をする際に、ずっと以前は UNIX のツールを使っていたと思います。awk、tr、sh その他もろもろ。
UNIX に触れたばかりの頃は「こういうツールの使い方を覚えなくてはならない」と考えていました。が、しばらくしていわゆる「スクリプト系言語」(大抵の場合はインタープリター式の言語)を使えば済むことに気がつきます。全部まとめて一つのスクリプト系言語でほぼ面倒をみれます。
そうなると、もう awk、tr、sh その他もろもろの使い方をマスターしようという気は綺麗サッパリなくなります、笑。

 それで書き捨てプログラム(あるいはスクリプト系の処理)に使う場合、scheme には一つ弱点があるように思います。scheme は正規表現が言語仕様上はサポートされていません。ダブルクオーテーション(文字列)の中で正規表現が使えません。
SRFI の関数なり処理系独自の拡張等で大抵の処理系では正規表現がサポートされています。が、少なくとも guile のは使いやすく無いです。
全部の scheme 処理系を試したわけではありませんが、guile を前提にするなら正規表現を使う処理は perl Lua 等を使った方が良いです。
正規表現が必要無い場合であれば、scheme を書き捨てプログラムに使うに際して困ることはないと思います。perl、lua で出来ることは基本的に scheme でも出来ると思います。

 なお、書き捨てプログラムに使いやすいというのを別の言葉で言い換えると「文法が単純明快でわかりやすい。習熟が容易である」ってことだと思います。



マニュアルは分かりやすいか
 これは好みが大きく分かれる話だと思います。
個人的な感覚ですが、いずれもオンラインマニュアルはしっかり書かれています。この3つなら「自分にも使える」という気になりました。



新版をコンパイル・インストールしやすいか
 うち(Debian-12、slackware-15)で試した限り3つとも簡単に新版をコンパイル・インストール出来ます。新しい版を追いかけるのは簡単です。



じゃあ、新版をどんどん追いかけても大丈夫なのか?
 変な問いかけです。
最近の各ディストリを見てると python、perl、lua などの so ファイル(シェアードオブジェクトファイル)が最初から入っています。
つまりこれらの言語をパッケージに組み込んでいると思われます。有名どころだと vlc は lua を使ってるようです。
scheme のうち guile についてはもしかすると slackware 等で使っているかもしれません。多のディストリでもパッケージをインストールしていくと、そのうち guile の so ファイルが入ってくるかもしれません。しかしそこまで詳細に確認はできません。
scheme でそれ以外の処理系の so ファイルは見かけません。
そうした場合、つまり最初から OS の中に so ファイルが入っていて、そこに別バージョンをどんどん入れていって良いかという問いかけです。
これについては、旧版とは別に新版を入れても支障ないはずです。so ファイルをロードする順は /etc/ld.so.conf で制御出来ます。なので自分が組み込みに使っている関係で新版から先に読ませたいと思ったら、そのように設定出来ます。
 この場合、ディストロのパッケージは旧版前提(slackware だと guile-3.0.7 )で作られていて、私が入れた新版(今なら guile-3.0.10 )を想定外に読むことになります。でも、ポイントリリース番号で3つ違うだけだなので問題が生じることは無いと思います。固定リリースのディストリなら大丈夫でしょう、多分、笑。
安心度は90%くらい。心配度は10%くらいだと思っています。(なお、CUI のサーバだと実際には guile を使ったパッケージは無いでしょう)

ただし、どうしても安心度を100%にしたいなら、常用処理系をディストリが用意している版に変える(新版は追いかけない)ことです。

同様の話として、常に100%の安心度が欲しい場合、かつpython、perl、lua 等を組み込みで使うなら新版を追いかけるのは諦めることになると思います。大抵どのディストリでもこの3つの so ファイルを最初から持っています。




結論は無しです。まとめも無しです。個別評価を足し合わせて各自ご判断ください。



 以下は私の感想というより、scheme に関する世間の受けとめかたについて感じることを書きます。
 scheme 処理系を選ぶときに R7 準拠のものが好まれるようです。ネットの書き込みを見てそう思います。
おそらくこれから scheme を始める方は R5 より R6、R6 より R7 が良い・・・と考えているのだと思います。が、本当にそうなんでしょうか?
ちょっと違うんじゃないかという事例を2つ上げてみます。
* まず一つ目は R5 で踏みとどまって R6 の考え方に全面移行することを拒んでいる(と見える)処理系が2つあります。chicken と guile です。
奇しくもこの2つは scheme 処理系の中で人気トップの2つです。R5 に踏みとどまっていることを好む schemer 達が多くいるってことだと思います。
* 二つ目は最近(ここ 10 年くらい)新規に処理系を作る上級のハッカーさん達は scheme をあまり選んでいないこと。15 年とか 20 年前だったら scheme を選ぶ人が多かったはずです。世の中の風(?)が変わりました。今は clojure が人気みたいです。
HTMLのプロジェクト管理的な機能を clojure は多く持っていてそこは Lisp/Scheme と clojure で全く違います。
でも一般プログラミング言語の部分はそのまま Lisp/scheme です。見てくれを少し変えただけで中身はそっくり。
なので、肝心なのは「それでも scheme を選ばず clojure を選ぶのは何故か」ってことです。
これは私の想像ですが Revised Report が引っ張っていく方向性を好まない人達が多いってことだと思います。
scheme を選ぶと Revised Report をどこまで受け入れるか個別判断が必要でしょう。でも clojure を選べばそういう判断無しに「ハッカーが作るハッカーのための言語」の道を容易に進むことが出来ます。
上の方で上げた Graham 氏の文章の中に「人気の言語を作るならハッカーに任せろ」ってニュアンスの部分があります。あれ、意味深な言葉です。
逆を読めば「学者に任せるとろくなことは無い」という意味にもとれます。Graham 氏は Revised Report の活動や方向性に批判的です。
こんな部分もあるので Revised Report のより高いバージョンを求めるのが良いとは限りません。ちょっと見る視点を変えるだけで結論は全く変わります。


 少し補足しておきます。「学者に任せるとろくなことはない」というのは私の主張ではありません。「Graham 氏がそう思っているかもしれない」という私の想像です。お間違いなきように、笑。
また、学者が作った言語でも Lua のように実用性をとても重視しているものもあります。
もう一つ、補足。言語としての scheme は R6 が色々(あまり良くない意味も含めて)影響していると思います。ただし幸いなことに処理系の作者さん達はそれほど影響を受けていない事例も多そうです。guile、chicken が R6 の影響を限定的にとどめています。国産 scheme でも gauche saggitarius など有名な処理系がありますが、「R6 風の学者の言語」ではなく「実用性を求めた言語」「ハッカーのための言語」の道を進んでいる様に見えます。目指している方向性がとても良いと思います。
scheme の場合は言語として評価するのと、処理系として評価するので大きな差があります。
gauche のウェブサイトに「scheme 処理系を選ぶときは、最初に慎重に選ぶこと。選んだらそれを使い込むのが良い」というニュアンスの事が書いてあります。示唆に富んだ良いアドバイスです。





Sec-4.速度の比較(簡易比較)

 比較方法

 こんなやり方です。書き込んだファイルは 500 行の文字列がソートされたものです。
この比較で分かるのはIO速度(ファイルから読んだり書き込んだりする速度)と文字列の比較関数の速さです。他の項目はこの簡易テストでは分かりません。
こんなんですが、マアマア、ちょっとした目安程度にはなると思います。

 また、アルゴリズムは単純2分木とします。アルゴリズムの優劣をチェックするなら、自己調整型2分木とかB木とか他にもっと優れたものがあります。
しかし今回の比較は同一プログラムを使って処理系間の速度比較するのが目的です。なので単純なアルゴリズムにしました。
500 行の文字列を含むファイルは http サーバのアクセス記録 500 行分を使いました。
実行環境は CPU が Ryzen3400G、メモリー16GB、Debian-12、Xfce で xfce4-terminal で実行しました。
(Slackware-15 で試すと、20%くらい Debian より高速でした)




 Debian での結果は以下のとおりです。
guile、chicken、chibi-scheme の3つの結果を比較しています。chicken についてはコンパイル版も示しています。guile はコンパイルされたものを自動的に使う仕組みです。
比較のために、同一アルゴリズムでつくった lua 版の結果も示しています。
キャッシュの影響を同一にするため、それぞれ3回実行して、そのうちのベスト値を掲載しました。
バージョンはいずれも 2025年初時点での最新版です。


guile の結果
$ time ./test-guile.scm < http500.log > data.guile
real    0m0.044s
user    0m0.032s
sys 0m0.013s



chicken インタープリター版の結果
$ time ./test-chicken.scm < http500.log > data.chicken
real    0m0.075s
user    0m0.061s
sys 0m0.013s



chicken コンパイル版の結果
$ time ./test-chicken2 < http500.log > data.chicken2
real    0m0.021s
user    0m0.012s
sys 0m0.010s



chibi の結果
$ time ./test-chibi.scm < http500.log > data.chibi
real    0m0.328s
user    0m0.311s
sys 0m0.016s





比較のために Lua の場合
$ time ./go.lua  < http500.log > data.lua
real    0m0.024s
user    0m0.020s
sys 0m0.005s






参考までに僭越ながら比較に使った scheme のプログラムです。適当にやってるので間違ってるかもしれません。間違っていたら、直して使ってください。

test パッケージ

chicken コンパイル版はインタープリター版の(load "tree03.scm") の一行を削除して、そこに直に tree03.scm のソースを埋め込みます。(全体で一つのファイルにしておく)
それを test-chicken2.scm とかして、その後、

$ csc test-chicken2.scm

とします。それで test-chicken2 という実行ファイルが出来るので、後は上記のように実行するだけです。
なお、http のログファイルは「どこの IP アドレスからどんなアクセスが来たのか・・・」など個人情報っぽい内容なので、上記 tar からは外しています。もしも自分で実行するなら、適当に文字列ファイルを調達してください。







本チャプターの古い版
https://www.quinos.net/topic8/topic8.01.html