Island Life

< ピアノレッスン135回目(終) | Gaucheでaobench再び >

2014/08/13

R6RSとR7RSのシンボル構文

Keiさん(@tk_riple)とやりとりしてて 思いだしたんだけど、R6RSとR7RSでシンボル中の文字をエスケープする構文に互換性が 無いのだった。

R6RS

シンボル中に \xNNN; という形でコードポイント NNN の文字を 埋め込める(桁数は任意)。通常のシンボルで許されない文字を使う時は必ずこの方式で エスケープしなければならない。

(string->symbol " ") => \x20;

なお、この表記は数多くのLisp方言の中でもR6RS独自の仕様。 また、この表記だと (string->symbol "") を表現できない。

R7RS

通常のシンボルで許されない文字を使う場合は、| で挟んで表記する。 この表記はCommon Lispで使われ、多くのLisp/Scheme実装でも採用されている伝統的なもの。

(string->symbol " ") => | |

|の中ではいくつかのバックスラッシュによるエスケープの他、\xNNN; 形式の エスケープも使える。こちらはR6RSの表記を取り入れた。

(string->symbol " ") => |\x20;|   ; これもok

ただし、|で囲まずにいきなり\x20;とは書けないのでR6RS式で エスケープされたシンボルは読めない。

で、どうしてこうなったんだっけ、という経緯を思い出せなかったのでちょっと探してみた。

SRFI-75

R6RSの、シンボルに特殊文字を含める方法については、 まず議論のために提出されたSRFI-75で検討された (これは元々withdrawされることを前提に、srfiのシステムで議論をしよう、という試み。)

タイトルからもわかるように、シンボルの構文だけでなく拡張文字セットについて、 どう扱うか、また文字、文字列、シンボル内でどう表記するか等を包括的に議論するもの。

で、忘れてたんだけど、この時点ではシンボルは伝統的に | で囲んで 特殊文字を入れるようになってたんだな。あと\xエスケープが固定桁で、 \xNN(2桁), \uNNNN(4桁), \UNNNNNNNN"(8桁)になってた (以前のGaucheと同じ)。

ちょっとこのsrfiは扱う範囲が広すぎて議論がひどく追いにくいんだけど、この中で

  • \x + 可変長16進数 + デリミタ」にしようぜ

っていう流れができた。なおデリミタが ; になった経緯はいまいちよくわからない。 \x{NNN}とか\x<NNN>とかの案もあった。あと、 ;だとエディタが対応するまでコメントとまぎらわしいからデリミタを別の文字に しようぜっていうもっともな話が出たんだけど尻切れとんぼになってる。

もうひとつ、

  • |で囲んで中でエスケープっていうのは、文字列が " で囲んで中で エスケープってのと同じだよね。
    • ってことは結局シンボルと文字列ってinternされてるかないかの違いなんだから シンボルをそんなに特別扱いする必要あるの?
    • 微妙に違うエスケープがあるのは綺麗じゃないね (縦棒シンボル内では | のエスケープが、文字列内では " のエスケープが必要)
    • いっそシンボルを #"..." にしちゃったら文字列と同じエスケープが 使えていいんじゃね?

てな具合に自転車置場的に議論が発散しつつ、最終まとめあたりでR6RS編集者が

  • シンボルを | で囲めば空白でも何でも入れられるよ、っていうのは、 かつてのLispに文字列が無くて何でもシンボルでやってた頃に逆行してね?
    • 識別子に空白を入れたいと思うのは何か間違ってるんじゃ
    • 読むときも空白で無意識に識別子を区切っちゃうから紛らわしいよね
    • どうしても空白入れたければ \xNNN; 表記があるんだから、 別表記を用意しなくてもいいよね

ってな流れになって、「\xNNN; は採用、縦棒表記は止めるかも」みたいな雰囲気に。

そして実際、リリースされたR6RSドラフトでは縦棒表記が無くなった。

なおこの時点で、R6RS式だと名前が空のシンボルが表記できない、ということは みんな失念していた模様。他のトピックでもさんざん議論しててみんな疲れてたのかね。

R6RS Formal comment

R6RSドラフトの段階でシンボルの字句構文が検討されたのは、 これしか見当たらなかった。

これ、Per Bothnerの直接の提案は、シンボル内で \ で任意の文字を エスケープ可能にしたらどうか、というもの。例えば

(string->symbol "abc def") => abc\ def

ただ、MLでこれが出たときにJohn Cowanが「|で囲う表記も復活させてほしい」 とコメントしてる。

これに対してR6RSエディタは上記のresolutionのとおり、 「シンボル表記中に空白が入るのは紛らわしい」との理由で却下。

この時も誰も「名前が空のシンボルはどうすんの」と突っ込んでないので見過ごされた模様。

R7RS ticket #304

R7RSはもともと新しいことを決めるよりもデファクトの共通部分を追認するという 基本方針。| で囲むのもデファクトだから入れよう、という話はわりと最初から あったと思う。

で、当初は | で囲うのとR6RS式と両方サポートしてたんだけど、 途中で「このために処理系拡張で使える文字を二つ (縦棒とバックスラッシュ) 使っちゃうのは 勿体なくない?」という意見が出される。

もともとSchemeは「独自言語作成のベース」という性質も持っていたので、 処理系が独自に拡張できる余地は多い方が良い。縦棒式とR6RS式は (空の名前を除いて)表現力に変わりはないのだから、どっちか一つで良いでしょう、 という流れに。で、どっちを選ぶかといえば 伝統かつより表現力の大きい縦棒だよね、という結論になった。

まあR6RSはたくさんのことを決めようとしすぎて、みんな何をどこまで議論したのか わけわからなくなってた感があるな。

非互換性は問題になる?

で、これがどのくらい問題になるかだけど、

  • コードについては、R6RSとR7RSでそれなりに書き分けることになるし、 共通化したればもともと共通部分だけで書かざるを得ないんだから、 シンボル名もエスケープ使わなけりゃいいだけだよね
  • データについては、リーダをR6RSとR7RSで分けるしかないね。
    • 尤もR7RS(-small)の場合、そもそも処理系がUnicode対応かどうかもわからないので、 「R7RS-smallの範囲内」で完全なポータブルを目指すならasciiの範囲内に留めておくしかない。 R7RS-smallはあくまでベースを提供するもんなんで、より大きな共通仕様が欲しかったら srfiとかで対応すればいいんじゃね、ってなノリ。
  • R7RS処理系が独自拡張でR6RS構文を読むことは全くの自由だから、 R6RS式が無視できないくらい流行れば対応してゆくでしょ。 そしたら現実的には問題無くなるだろ。

って感じかなあ。R6RSとR7RSで仕様として目指すものが違うので、 一方の立場でもう一方を判断しようとするとおかしなことになると思う。

Tags: Programming, Scheme, Lisp, R7RS

Post a comment

Name: