このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

即値と参照:基本的/本質的だと思うから再論

JavaScriptとの関連で、即値と参照の説明をしたことがあります。「即値」という言葉は、本来は別の文脈で使われるものですが、語感がたいへん適切だと思って僕が「参照型ではないデータ」の意味で使ったのです。詳しくはこのエントリーを見てください。

さて、ここではJavaScriptに限らず一般的な観点から即値と参照を再論します。この話題は、データ構造やプログラミング言語を論じる際の基本(のひとつ)になると思うからです。

内容:

  1. なんで即値と参照の区別があるのか
  2. 整数は即値か参照か
  3. ミュータビリティに注目せよ

●なんで即値と参照の区別があるのか

即値と参照を区別する習慣は、現状のハードウェアや言語処理の方式に由来するものです。例えば、多くの実装で、関数に引数を渡すにはスタックに積み、戻り値はレジスタに格納します。スタックに大きなデータをコピーするのはうれしくないし、レジスタにはサイズの限界があります。という次第で、でかいデータの本体はヒープ上にそのまま置き、そのアドレスだけをあっちこっちに移動やコピーしたほうが都合がいいことになるわけです。これが参照の由来と動機(だと思う)。

現在(あくまで今このとき)の多くのコンピュータは32ビットデータの扱いが得意だから、32ビットアドレスの移動やコピーは高速/容易に行えます。が、こういう“仕掛け”の都合に依存した概念や区別が意味あるんでしょうか? 低水準プログラミングでは意味ある、つうか、バイトやアドレスを生々しく扱いたいのだろうから、当然な区別ですが、(ある程度以上の)高水準言語では邪魔なだけだと僕は思います。

●整数は即値か参照か

ある架空の例を考えて、即値と参照の区別が不自然であることを示しましょう。

整数の算術演算ができるミニ言語Miniがあったとします。この言語Miniには整数型というデータ型があります(変数に型宣言がないとしても)。Miniの整数はハードウェアに好都合な32ビット整数として実装したとしましょう。この時点では、Miniの整数型は即値型です。

ところが、天文学的な巨大数を計算するニーズが生じて、無限多倍長整数(BigNum)を導入しました。しかし、単純な言語仕様がウリのMiniに、small integerとbig integerの2つの型を導入することは許容できないので、どんな整数でも参照で扱うことにしました。この時点で、Miniの整数型は参照型に変わりました。

さらにところが、いつでも天文学をしているわけではなくて、ほとんどの整数計算は日常的整数であることが判明して、Mini実装者は整数データ/整数計算の効率化に挑みました。ヒープ上にアロケートするMiniのオブジェクトは最低でも64ビット=8バイトのサイズを持つとしましょう。8の倍数ごとにオブジェクトを配置することにすれば、32ビットアドレスの下位3ビットはいつでも0なので、余分な情報になります。もったいないのでこの3ビットに次のルールでメタデータを詰め込むことにします。

  • 000 なら、残りの29ビットに符号付き整数が入っている。
  • 001 なら、残りの29ビットに符号なし整数が入っている。
  • 010 なら、残りの29ビットの一部にUnicode文字が入っている。
  • 100 なら、文字列オブジェクトへのポインターである。
  • …… ……

要するに、0xxなら即値、1xxなら参照で、xxの2ビットでタイプを分類しているのです。下位3ビットで判断し後、ビット演算を使って値を取り出すなりポインターを作るなりします。

この方式では、29ビット以下で表現できる整数に関しては即値で、それを超える整数は参照で扱うことになります。この時点で、Miniの整数型は即値型と参照型の2種類になってしまいました。

●ミュータビリティに注目せよ

整数の範囲(扱える最小値と最大値)や計算速度/メモリ消費量が変わるタイミングで、整数型の分類が即値になったり参照になったりするのはどうも変でしょう。実装とは独立であるべき言語仕様のなかに、このデータは即値として表現される(すべき)とか、参照に限るとか含めてしまうのは、あんまりいい考えじゃないと僕が思う根拠はこれです。

しかし、「整数値と整数カウンタ、文字列値と文字列バッファを区別するな」とは言ってません。むしろ、これらの違いこそが重要なのです。整数値は状態を持たないし、個体識別もしません、文字列値も同じです。その意味で“値”なのです。値として取り扱うデータは、空間(メモリ上の位置)や時間(生成、状態遷移、消滅)の概念を持ってはいけないのです。

一方で、通常のオブジェクトは固有な位置(したがって個体識別)と寿命(したがって状態遷移としての“人生”)を持ちます。オブジェクトを値として使うには、イミュータブル(変更不可能)にすればよいので、結局、データの区別として重要なのは、ミュータブルかイミュータブルかってことです。

従来の参照/即値の区別も、気持ちとしてはミュータブル/イミュータブルが入っていたと思いますが、「参照か即値か」と「ミュータブルかイミュータブルか」が直接対応するわけではありません。“すべてのデータを参照で扱い、すべてのデータがイミュータブルである言語”も考えられます(純粋な関数型言語の最適化しない実装はそうなるはずです)。

というわけで、「僕はミュータビリティに注目せよ」って言いたいわけね。だけどこれは、2 + 32.add(3) と書けたほうが良いの悪いの、ってな議論とは何の関係もないことは注意しておきます。