Island Life

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

2014/08/02

ピアノレッスン135回目(終)

今の先生とはこれで最後のレッスン。

  • Ginastera: Tres Piezas - 1. Cuyana
    • 先週はただ物悲しいだけだったけど、今回は色々な風景が見えた。短い曲の中にたくさんの景色が詰まっている。
  • Beethoven: Waldstein 1st mvmt
    • 今日の演奏は、ピアノ曲というよりシンフォニーのように聴こえた。Good job.

特に課題を出されるということはなく、ひたすら 自分のやりたい曲を持っていって聴いてもらってヒントを授かる、というスタイルだったけれど、 この3年で「見える風景」がかなり変わったように思う。

Tag: Piano

2014/07/26

ピアノレッスン134回目

  • Ginastera: Tres Piezas - 1. Cuyana
    • 不協和からの解決が綺麗
  • Beethoven: Waldstein 1st mvmt
    • ほぼ暗譜。
    • 左手旋律で右手が細かく動く時、聴衆も右手に耳がいきがちなので、非常に軽く。特に 第二主題で左に旋律が移るところ。

Tag: Piano

2014/07/23

先ずデータありき

前エントリ「プログラムは書いたとおりに動くのか」について、 一部「ああ、データ構造+アルゴリズム=プログラムってことね」というような反応があったんだけど、ちょっと違う。

プログラマ的視点における「データ構造+アルゴリズム」では、プログラマが問題にふさわしいデータ構造を決めるって意識だと思うんだけど、あのエントリで言いたかったのは、プログラマが考え出す前に既にデータそのものがあるってこと。

もちろん何らかの構造を決めないとコードでは扱えないんで、データ構造は作るんだけど、それは無から自由に作るもんじゃなくて、既にある塊の中にある構造の一面を切り出す、みたいな作業になる。まあ、それが結局モデル化って呼ばれる作業なんだけど、しばしばこのモデル化ってのが、具体的なインスタンスからイデアあるいはアーキタイプを見出す作業のようにとらえられてるんじゃないかって気がする。

目前にある具体的なデータは、ある「元型」が具象化したものだから、その「元型」を見つけましょう、それをクラスなり型なりとして記述しましょう、というアプローチ。

でも現実のプログラムの多くはそうじゃないんじゃないか、ってのが言いたかったこと。現実のデータに「元型」なんてなくて、データはデータとしてごちゃっとそこにある。プログラマが設定するクラスなり型はあくまでその混沌を一つの投影面に射影して何とか扱えるようにした暫定的なもので、別の目的なら全く違った型が見えるかもしれない。でもそれぞれが触るインスタンスは同じ具体物だ。本来の情報の流れはインスタンス→クラス、であって、イデアたるクラスが大元にあってインスタンスを産み出しているわけではない。プログラムコードとしてはクラスからインスタンスを作ってるんだけど、それは便宜上そうしてるだけ、って感覚を主張したかった。

んで、このデータっていうのはプログラムより前からそこに存在するだけでなく、プログラムの都合とは関係なしに変化する。それに伴って構造も変化する。

データ構造が先にありき、の感覚だと、変更を主導するのはプログラマだ。けれど、データが先にあるという感覚だと、データ構造は常に暫定的であり、データが変化したらすいっと構造の方を追従させてゆく必要がある。変化については、能動的なのはデータであり、受動的なのがデータ構造を含めたコードなのだ。

一部のプログラミング言語では、実行中にクラス定義を変えたり、 インスタンスが属するクラスを全く違うものにすげ替えたりできる。 これは静的な検証という観点からは最悪の機能だ。だってT型のデータを受け取って、 それをT型を期待する引数に渡すってコードがあっても、その間にデータ自体の型が S型に変化する可能性があるのだから。

でも、特定の設計の適用範囲よりもデータの方が長生きする、という立場に立った場合、 目前にある混沌としたデータをとりあえず触れる形にしておいて、 必要に応じて表現を動的に変えてゆく、 プログラムコードは常にその時点の暫定版に過ぎない、 という見方も可能なのだ。

(別に静的型をdisってるわけじゃなくて、立場の違いってこと。 なお、上で述べたような「混沌としたデータが、投影法によって違うものに見える」 というのは、型クラスとかtraitのような仕組みと相性が良いと思う。)

Tag: Programming

2014/07/21

プログラムは書いたとおりに動くのか

もちろん、コードはバグも含めて書いてあるとおりに動く。 だからプログラマは自分の意図をなるべく無駄なく正確に記述出来る言語を望むわけだ。 自分が思うとおりにプログラムを動かしたいからね。

けれども、プログラムの振る舞いというのは、書いてあるコードだけでは決まらない。 矛盾するようだけど、「決まる」のレイヤが違うのだ。

プログラムの出力は、当然、コードだけでなく与えられたデータで決まる。 プログラムの目的が、ただデータを受け取り処理して出力するだけなら、 その処理過程はコードにすべて記されているのだから、コードがすべてと言える。

けれども現実のプログラムのほとんどは、 プログラムとしての入出力の他にもデータを抱えていて、 プログラム総体としての動作はコードだけでなく内部データにも依存している。 プログラマとしては、自分の責任はコードが仕様通り動くところまでだよ、 望む動作をさせるために必要なデータは各自用意してね、と言いたいところだけど、 ユーザから見れば、どこまでがコードで決まりどこから内部データ=内部状態に依存しているかは どうでも良いことだ。

昨今は、プログラムがユーザから見て「正しく」動くためにこの内部データの 比重がどんどん増しているように思う。例えば統計的スパムフィルタ。ユーザから見た 「正しい動作」とは、ユーザがスパムだと思うメールを弾いて、ユーザがそう思わない メールを通すことだ。コードだけ見ていくら正しさを検証したとしても、 ユーザにとっての正しい動作は保証できない。ユーザにとっての正しい動作に 近づけるには、データの性質に沿うようにコードを変形させ適合させてゆく必要がある。

また、コードとデータの境界というのは、それほどはっきりしているものでもない。

ゲームのエフェクト。アーティストが用意したデータファイルを読み込んで表示するようにするか、 プログラム的に生成するか、一部は素材として読み込んで一部はプログラムで生成するか、 どこに線を引くべきかは、開発リソースにも依存するし、どんな出力が欲しいかによっても変わってくる。 出来を見ながら試行錯誤する段階で、線引きがころころ変わることもある。 実装上も、外部ファイルから読んでいればいかにもデータっぽいが、 プリプロセッサ通して静的なデータとしてプログラムにリンクしたり、 さらにデータからパラメータを抽出してそれを埋め込んだコードを生成したり、 とやってゆくとどこまでがコードかわからない。

言語処理系をいじってるとコードとデータの境界は相対的なものにしか見えなくなる。 Scheme処理系にとって、処理対象のSchemeプログラムはデータだ。 GaucheがSchemeスクリプトを読んで何か意味のある動作をする時、 Gaucheに着目すればSchemeスクリプト自体は内部データだが、 Schemeを見ればそれはコードだ。しかも総体としてのプログラムにとって、 Schemeコードが実行時解釈されているかコンパイルされてコードの一部になっているかは どこまで部分評価してるかという程度問題で、ユーザにとっては意味をもたない。

★ ★ ★

さて。自分の書いたコードについて、その動作を隅から隅まで把握し、 正しさを保証したい、と思うのはプログラマとして無理からぬことである。 静的型システムや、副作用の排除といった枠組みはそれを助けてくれる。

でも、それで完璧に正しいコードが書けたとしても、 プログラムの動作を決めている内部データの方がうんと大きかったら、 それで保証される正しさはごく限定的なものになる。

むしろ、現在のソフトウェアの趨勢を見ていると、未来のプログラムのあり方というのは、 何だか良く分からない可塑性のある膨大なデータが永続的に存在していて、 各種プログラムは後付けでそこにそれぞれの意味を見出して寄生するような形に なるかもしれない。

入力データの仕様と出力データの仕様が決まってからプログラムが書かれるのではなく、 プログラムを書きながら、バージョンアップを重ねながら、 当初見えなかったデータの「かたち」を彫り出して、 思いもかけなかった出力を得てゆく、 そういう「プログラミング」の重要性が増してゆくかもしれない。

Tag: Programming

More entries ...