大人のためのブラックボックス読解講座――クロージャとオブジェクトの微妙な関係(その2)プログラミング言語の進化を追え(3/3 ページ)

» 2007年03月30日 12時00分 公開
[川合史朗,ITmedia]
前のページへ 1|2|3       

型としてのオブジェクト

 ここまで見てきたように、クロージャとは、「状態と処理を合わせたもの」にとどまらず、むしろパラメータライズされた処理のブロックを記述する汎用的な手段でした。一方、オブジェクトの方も単なる「状態+処理」だけにとどまるものではありません。オブジェクト指向の特性とされる性質のうち、本稿で挙げたようなクロージャの使い方ではカバーできないものがあります。中でも重要なのが、型としての側面です。

 静的型を持つクラス指向言語では、「クラス」はインスタンスのプロトタイプを指定するだけでなく、新たな複合型を定義するという性質を持ちます。「あるオブジェクトに対して、それが持つ状態と適用可能な操作の集合が明示的に宣言されている」ということです。

 クロージャによる状態とメッセージの表現では、メッセージによって返されるクロージャの型がまちまちなため、静的な型解析がひどく厄介です。最適化やコンパイル時のエラー検出を追究していくと、すぐにStalinのようなプログラム全体の解析まで踏み込まざるを得なくなります。


 クロージャをベースにしたアプローチを使っている場合、プログラマーはまず個々の操作に注目し、状態は、「必要なものが後から自然についてくる」といった感覚を持っているように思われます。

 動的型付けのオブジェクト指向言語の場合、コンパイラによる型の解析という面では、オブジェクトからのアプローチによる利点を直接享受できません。それでも、その言語で考えているとき、プログラマーの頭の中には特定の「状態と操作の集合としての型」が存在するようです。

ハイブリッドの意味

 プログラムというのは、それ全体が「状態と処理のひとかたまり」です。クロージャとオブジェクト指向はともに、その大きな塊を扱いやすい小さな塊へと分割してゆく手段です。どちらも最終的に得られるものは、小さな「状態+処理」の塊であって、その意味では「クロージャ≡オブジェクト(mod構文)」であると言えます。

 その一方で、そう言いきってしまうことに違和感を覚えるのは、分割していくアプローチに違いがあるためでしょう。幾つもの操作の中から共通した操作を見つけてくくり出していくクロージャベースのアプローチと、共通した操作が適用できる状態の集まりを先に探そうとするオブジェクト指向ベースのアプローチ、という傾向がありそうです。

 どちらのアプローチをメインに据えるかに差はあれど、現代のプログラミング言語のほとんどは、両方のメカニズムを備えたハイブリッドなものになっています。プログラマーとしては、両者のアプローチの違いを頭において、問題に適したメカニズムを選択していくことになるでしょう。

言語設計者にとってのハイブリッド

 ところで、言語設計者、あるいは処理系設計者として、この2つのメカニズムをどのように調和させるべきでしょうか。オブジェクト指向言語がクロージャのメカニズムから、あるいはクロージャ中心の言語がオブジェクト指向言語のメカニズムから採り入れられることはあるでしょうか。

 クロージャベースの、特に動的型付け言語においては、操作を束ねるやり方として動的ディスパッチのみに頼るのではなく、「共通の操作の集合を宣言できるようなメカニズムを採り入れることでプログラマーの頭の中が整理しやすくなる」ということが考えられます。Haskellの型クラスのようなものがヒントになるかもしれません。

 オブジェクト指向言語については、「メモリ上に連続して配置されている、状態のひとかたまり」と「状態と適用可能な操作の論理的な集合」との結びつきをもう少し緩めてはどうかと思います。ハードウェアアクセスなど低レベルなレイヤーを除いては、現代のプログラミングでオブジェクトを物理的にひとかたまりに配置しておく必要はあまりありません。この2つを切り離すことで、過去30年にわたり蓄積されてきた関数型言語のプログラム解析手法を、より自由に適用できるようになるのではないでしょうか。

より良いプログラミング言語を目指して

 なお、本稿では「オブジェクト指向」「オブジェクト」という用語をあえて厳密に定義せず、それぞれ、

  • 現在主流となっている、クラスベースあるいはインスタンスベースのオブジェクト指向言語処理系が採用しているメカニズム
  • そのメカニズムにより実体化される「状態と処理を合わせたもの」

のように考えて筆を進めました。

 前述したように、「オブジェクト指向」の定義は人により異なるためでもありますが、「言語に両方のメカニズムを採り入れることの意味について包括的に考えてみたかった」というのが主な動機です。切れ味の良い結論と言うより、オープンクエスチョンのようになってしまいましたが、これからのプログラミング言語を考えてゆく上でのヒントになれば幸いです。

このページで出てきた専門用語

コンパイラが手を抜いているか、のいずれかなので、気にしても仕方がない

(2)に関しては、コンパイル時間やデバッグの利便性からあえて最適化を行わないという選択をしている場合も多く、コンパイラが駄目だというわけではない。ただ、実用的に使いこなしたければ「コンパイラの癖」のようなバッドノウハウを知る必要はある。

リスト2のように書けます

この例では返されるリストの要素は与えたリストの逆順になる。集合として考えるなら順序は関係ないが、順序を保存したければ最後にリストをreverseすれば良い。

元のsum-of-numbersとmore-thanを再構成できます

sum-of-numbersに渡しているクロージャは、この場合単に関数「+」を同じ引数で呼び出しているだけなので、「(fold + 0 numbers)」と書いてしまっても良い。

Gaucheの組み込関数disasmで逆アセンブルしてましょう

本稿に上げたfoldを定義せずに「(disasm fold)」を評価すると、同名の組み込み関数foldの方の逆アセンブル結果が表示されることに注意。組み込み関数の方は可変長の引数を取れるため、その分コードが大きくなっている。それでもクロージャのアロケーションは行われない。

Jeffrey Mark SiskindによるStalin

Stalin: STAtic Language Imple

mentatioNの略、Dylanに対抗して名づけられたらしい。こちらに最新版0.11へのリンクがある。

define-syntax構文

Scheme仕様で定められているマクロシステムの1つ。define-syntaxは、最新のScheme仕様であるR5RS(Revised5 Report on Algorithmic Language Scheme)で定められた。


本記事は、オープンソースマガジン2006年10月号「プログラミング言語の進化を追え」を再構成したものです。


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ