ちょっと特異な特異メソッド入門

まえせつ

「ユーザの声」の第 2 回、さっそく、ご寄稿いただいた中島拓さんをご紹介します。 (編集:すぎむし)

中島さんは数年前から、Ruby を利用した Web アプリケーションや関連のライブラリを数々開発され、それが

  • 1CD 上に Web サーバーを構築する「鋼鉄サーバー」プロジェクト
  • その一環としての Web アプリケーションフレームワーク、walrus
  • そこから派生した(X)HTML テンプレートエンジン、amrita
  • MS Windows マシンで tDiary と Hiki を動かせる「ブログ体験キット」(HTTP プロキシーサーバである PROXY-2004 という製品に附属)

などの、いずれも先駆的業績に結実しています。

どうして先駆的であれたのでしょう?  ひとより早く確かな結果を出せる、本人の腕が優れていたこともあったでしょう。 企業内で Ruby アプリケーション開発に携われる、恵まれた環境もあったことでしょう。 しかし、何よりも、先駆的に「何に」手をつけたらいいのか、というところへの確信の深さがあったのではないでしょうか。 その時その時、何に注目するか、どこから手をつけるか、という判断の裏には、 中島さん独自の深く鋭い視点があるのではないか、そう見ています。 以前公開された原稿

  • 2002 年の Linux Japan (惜しくも休刊) への寄稿

にも、そんな視点の片鱗がうかがえるような気がしました。

そのとき以来の Ruby の記事でしょうか? 力作が届きました。

ちょっと特異な特異メソッド入門 (中島拓さん)

 あるいは、どうしてこのくさい新聞紙は今日もいつもと同じ速さで積みあがっているのか

はじめに

Ruby には、非常に言葉にしづらい不思議な魅力があると思います。 Ruby をよく使うプログラマの大半がそれを感じているのに、なぜか、そこをうまく言葉にした人がいないような気がします。 この文章は、それをなんとか言葉にしてみようという試みです。 そのために、社会システム理論という、ちょっと畑違いの分野から「複雑性の縮減」という概念を借りてきて使っています。

社会システム理論というのは非常に難解な学問で、私自身それを正しく理解しているとは思えないのですが、 自分なりに「こうかな」と思う所はあって、その「こうかな」という理解でこの概念を使うと、 言語化しづらい Ruby の魅力がうまく説明できるような気がしています。

ちょっと遠まわりになりますが、 まず、「複雑性の縮減」という概念について、その「こうかな」という勝手な理解から説明させていただきます。

「朝の儀式」に「複雑性の縮減」を見る

私は、毎朝、トイレで新聞を読みます。 何年も続けているので、これが習慣というか「朝の儀式」になってしまってます。 ひとつは、腸の健康のためなんです。 どうも私の場合、無理をすると疲れが腸の問題として出てきやすいので、 朝のトイレにある程度の時間をかける必要があるんです。

それで、なんでこれが「朝の儀式」として成り立つかと言うと、 新聞というのは、適度の分量で、適度に興味のある記事があるからです。 しかも、そのページ数も内容もある一定の範囲に収まっていることを期待できます。 特にニュースがないからと言って、ページを減らされてしまうと、 私の朝のトイレが短くなって、腸の具合に響いてしまう。 また、大事件があったからと言って 100 ページもある新聞が来たら、 トイレの時間が長くなりすぎて、遅刻してしまいます。

毎朝、一定のページが配達されてきて、 その中で、自分の読みたいと思う記事の分量というか割合もだいたい一定です。 だから、自分が読みたい記事だけをひととおり読む時間もほぼ一定になることが期待できて、 それが、トイレにかけるべき時間と一致しているわけです。

しかし、これは考えてみると不思議なことです。 世界では毎日毎日、予測不可能なさまざまな事件が起きていて、 それについて知りたくて私は新聞を読むのですが、 その激動する世界が、私の健康を維持するための習慣につながっているわけです。

つまり、ここには謎が二つあります。

  • 世界が変動してさまざまな事件が起きているのに、なぜ、私のトイレの時間はほぼ一定なのか?
  • なぜ、私のトイレの時間が一定なのに、その一定の時間内に私は毎日激動している世界とつながることができるのか?

「ルーマンの社会システム理論」では、 ここで起きていることを、複雑性の縮減 (reduction of complexity) と言う概念によって理解しようとします。

つまり、世界には、人間に把握できないほどの複雑性がある。 さまざまな人がいて、さまざまな事件が起きている。 それが、ダイレクトに人間に襲いかかってきたら、人間はその複雑性を処理できない。 世界の激動がダイレクトに新聞の内容やページ数につながっていて、 新聞が世界のように予測不可能であったとしたら、 私のトイレの時間はメチャクチャになって、私の腸が悲鳴をあげてしまいます。

世の中には、トイレでゆっくり新聞を読むこともできないかわいそうなお父さんも多いかもしれませんが、 そういうお父さんも新聞のフォーマットやページ数やスタンスがある一定の範囲におさまっていることは、 期待していると思います。 むしろ、そういうふうに、多種多様な事件をある一定の枠組みにおさえこんで、 読者がそれを理解しやすく、受け入れやすい形にするのが、新聞に期待されている機能だと思います。

「複雑性の縮減」のパラドックス

ここで、新聞には、相反する二つの要件が求められています。

  • 世界が変動しても新聞が変動しない (変動が一定の範囲から逸脱しない)
  • 世界の変動を新聞の中に反映させる

つまり、新聞は世界の複雑性を写しとったモデルではあるんだけど、 その複雑性を減らした (縮減した) モデルである必要がある、つまり世界よりは単純でないと意味がないということです。 そこにバランスが求められています。

新聞は、ある程度、政治的なスタンスや重視するジャンルを固定する必要があります。 それによって、読者は自分のそれと一致する新聞を選択することができる。 しかし、あまりにも硬直化していて、 どんな事件にも、ステレオタイプな見方しかできなくて、分析より非現実的な批判が過ぎてしまうと、 世の中で何が起きてどこへ向かっているのかわからなくなって、 読者は新聞を読む意味を失なってしまいます。

一般に、複雑な環境 (世界) の中に存在しているシステムは、 このような問題に直面しています。 世界の動きに追随できないと環境に適応できないし、 世界の動きに振り回されて自分を見失ってしまうと、自分が崩壊してしまいます。

新聞社自身も、そのように激動する世界の中で適応を模索するシステムであるし、 我々読者も激動する世界の中で適応しようとするシステムであって、 その適応のためのひとつのツールとして新聞を読むわけです。

それで、ルーマンによると、このようなシステムが存続するためのひとつの条件が、 ある程度、システム自身が複雑であることです。 環境の複雑さに対応するだけの複雑さをシステム自身が持ってないと生きていけません。

新聞の編集部全体が、特定の思想信条に偏っていたり、 特定の個人の力だけに頼っていたりすると、 その内部では揉め事が少なくて、物事がスッキリ進むでしょうが、 そこから出て来る新聞は、環境との接点を失ったものになってしまいます。 何の事件が起きても毎回同じような社説が出ていて、 それぞれの事件で何が問題なのかわからなくなったりする。 あるいは、単なる事実の羅列で、分析や比較がなくて、 その事実をつなぎ合わせて考えるのが読者の仕事になってしまう。

つまり、内部が単純で一枚岩であると、 外部からの複雑な入力を複雑なままパススルーするか、 過度に単純化してしまい外部との関連が切れた独り言になってしまうか、 そのどちらかの極端な方向に傾いてしまいます。 複雑性の縮減を適切に行なうためには、内部に複雑性をかかえこんでいる必要があるわけです。

実際に、新聞がどのように事件を選択して記事にして紙面を作るのかということは、 多くの人がかかわる、非常に複雑な手続きです。 おそらく、それを簡単に説明することはできないし、 新聞社の内部でも特定の個人がその全体像を把握しているかどうかも怪しいのではないかと私は思います。 その新聞社の複雑性によって、新聞は世界の複雑性を縮減して、毎日 32 ページの均等な紙面を出力し、 私の腸の健康が保たれるわけです。

「複雑性の縮減」と情報システム

長々と、るびまという技術関連の紙面で Ruby と関係ないような話を繰り広げておりますが、 勘のいい Rubyist の方なら、そろそろこれがどこへ着地しようとしているか見当がついてきたのではないかと思います。 つまり、ここで私が述べている「複雑性の縮減」というのは、 情報システムの機能でもあるし、プログラミング言語の機能でもあります。 見方によっては、「複雑性の縮減」こそが両者の本質とも言えるものです。

次に、情報システムにこの概念をあてはめてみましょう。

Google や 2ちゃんねるのような Web サイトは、 新聞と同じように、我々が世界を見るための窓として使用されています。 そこから出てくるページは、次の二つの期待に答えているわけです。

  • 世界の複雑性を反映していること
  • しかし、ある特定のフォーマットにおさまっていること

まさに、ここで期待されている機能は、「複雑性の縮減」ではないかと思います。 これは、企業の業務を遂行する業務システムでも同じことが言えます。 業務システムは、企業自身や顧客や取引先等の環境のダイナミックな側面をリアルタイムに反映しつつ、 それを一定の複雑性の範囲に収め、特定の決まったフォーマットに変換して情報を提供しなくてはなりません。

そして、大半の情報システムは、単なるプログラムの集積でなく、 それをメンテナンスする人間と一体化したシステムとして動作することで、 そのような期待されている機能を実現しています。

Google は、ページランクという単独のアルゴリズムで動いているシステムとして登場しました。 他の検索エンジンと比較すると、人間の関与が少なく機械的に実行される要素が多いことで、 コストやスケーラビリティの面で優位に立ちました。

しかし、プログラムによる機械的な処理だけでサービスを維持できたのは最初だけです。 ページランクというアルゴリズムは SEO 対策以前には調整なしで非常によく機能していたと思いますが、 現在の世界は、Google 以前の世界より複雑です。 そこには SEO という Google 以前の世界には存在しなかった要素が含まれており、 これに対応するために、Google は不断にアルゴリズムやデータベースを改変していく必要があるわけです。 単なるコンピュータシステムでなく、そのメンテナンスを行なうエンジニアを含んだ、組織+コンピュータシステムが Google というシステムです。 その中では自律的に動く多数の優秀な技術者がいて、 緊密な共同作業をしています。 それは非常に複雑なシステムだと思います。

そのような複雑なシステムでなければ、世界の複雑性に対応できず、 「複雑性の縮減」という機能を果たすことはできません。 そして、プログラミング言語の最も大きな困難は、 「複雑性の縮減」を人間抜きで、 すなわち、形式的に定義可能で機械的に実行可能な手続きとして行なわなければいけないということです。

「複雑性の縮減」とプログラミング言語

(ようやく Ruby の話になりますが)、 プログラミング言語というものも、 新聞や Google 同様、世界の複雑性に対応して、「複雑性の縮減」という機能を果たすものです。 世の中で必要とされているプログラム、システムは非常に多様であって、 多くのプログラマにとって、その複雑性は、自分の適応可能な限界を超えたものです。 徒手空拳ではそれに対応できないので、 プログラマはさまざまなツールを使うわけですが、 最も重要なツールがプログラミング言語です。

つまり、新聞が世界の事件の「複雑性を縮減」するように、 Google が世界の Web ページの「複雑性を縮減」するように、 プログラミング言語は、情報システムの「複雑性を縮減」するツールです。 あるいは、プログラマーがプログラミング言語というツールをシステム内部の作動原理として使用することによって、 要件の「複雑性を縮減」していると言う方が、システム理論的には厳密な表現かもしれません。

Ruby という言語の魅力は、このような意味で、「複雑性の縮減」のためのツールとして見た場合に、 本質的な意味の「複雑性」を内部にかかえこんでいる所だと思います。

プログラミング言語をシステム理論的に見た場合、 何をシステムの境界と見なすかについていろいろな見方ができます。 開発者やコミュニティを含んだ人間系を含んだ変化、発展するシステムとして見ることもできますが、 一般のプログラマにとっては、処理系というプログラムが単体で、人的介入のない単なる機械的な手続きとして立ち現れてきます。

「複雑性の縮減」を行なう為には内部が複雑である必要がありますが、 新聞社のように人間を含むシステムでは、人間の相互作用によって複雑性を確保することができます。 Google は、会社自体がひとつのシステム、ひとつのアルゴリズムを中心に編成されていますが、 時間の中で進化していく過程を考えると、少数精鋭のプログラマの役割は大きく、 やはり、その複雑性は人間によって担われています。

プログラミング言語は、世界の複雑性を自分自身の中に写し取る時に、 人の助けを借りることができません。 形式的に定義可能な言語仕様や、機械で実行可能な処理系の中に、 あたかも複雑性があるような幻想を与えなくてはなりません。

しかも、それはプログラマーに理解可能で、現実的なスピードで動作するよう実装されなくては無意味です。 理解可能であるためには、概念の数を減らしてなるべく少ない原理で全体が統一されていなくてはなりません。 かといって、あまりにスッキリしていては、スッキリしてない世界の複雑性とうまくマッチしません。 そういう、根源的なパラドックスを含むような困難な課題に、全てのプログラミング言語は直面しています。 そして、Ruby という言語は、その問題を非常にうまく解決しているように思えます。

これは、特定の機能や文法のような部分に現れるものではなくて、 言語全体としてテイストの中に見えてくるものなので、 非常に説明しにくいものですが、 ここでは、「特異メソッド」という機能を、そういう観点から分析してみたいと思います。

サンプルプログラム (七並べ)

次のサンプルコードを見てください。 これは「七並べ」のようなカードゲームのプログラムの断片です。

 class Card
   attr_reader :suite, :number

   SPADE = Object.new
   DIAMOND = Object.new
   HEART = Object.new
   CLUB = Object.new

   def initialize(suite, number)
     @suite,@number = suite, number
   end

   def can_be_next_to?(other)
     other.suite == self.suite and
       ( other.number + 1 == self.number or
         other.number - 1 == self.number )
   end

   Joker = Object.new
   def Joker.can_be_next_to?(other)
     true
   end
 end

 if $0 == __FILE__

   c1 = Card.new(Card::CLUB, 1)
   c2 = Card.new(Card::CLUB, 2)
   c3 = Card.new(Card::CLUB, 3)
   h2 = Card.new(Card::HEART, 2)

   p c2.can_be_next_to?(c1) # → true
   p c3.can_be_next_to?(c2) # → true
   p c3.can_be_next_to?(h2) # → false
   p c1.can_be_next_to?(c3) # → false
   p c1.can_be_next_to?(c2) # → true

   j = Card::Joker
   # ジョーカーはいつでも場に出せる
   p j.can_be_next_to?(c1) # → true
   p j.can_be_next_to?(c2) # → true

   bafuda = [
     Card.new(Card::CLUB, 7),
     Card.new(Card::SPADE, 7),
     Card.new(Card::HEART, 7),
     Card.new(Card::DIAMOND, 7)
   ]
   tefuda = [ Card.new(Card::CLUB, 8), c1, c2, Card::Joker ]

   ok_cards = tefuda.find_all do |t|
     bafuda.find do |b|
       t.can_be_next_to?(b)
     end
   end
   printf("手札の中に出せる札が%d枚あります\n", ok_cards.size)
   # → 手札の中に出せる札が2枚あります
 end

Card というクラスの、can_be_next_to? というメソッドは、 場にあるカードに対して、自分の手札を出せるかどうか判定する時に使うものとします。 種類が一致していて、かつ数字が連続していればいいというだけの単純なロジックですが、 ジョーカーをどうするかという問題があります。

手札にジョーカーがあれば常にそれを場に出すことができます。 従って、ジョーカーに対して can_be_next_to? を実行したら、その結果は常に真です。 そのロジックはこれ以上ないくらい単純な話ですが、 問題は、それをどのように (どこに) 実装するかです。

普通の静的な型を持つ言語では、ジョーカーに対応するクラスを作ることになると思います。 そして、ジョーカークラスのインスタンスは一つに限定されることをシングルトンパターンで表現するのが一般的な解法だと思います。

しかし、Ruby ではクラスを介さずに、特定のインスタンスにメソッドを定義することができます。 それが特異メソッドです。 この場合は、Card::Joker という定数に特定のインスタンスを割りあてて、 それのインスタンスに直接メソッドを定義しています。

 Joker = Object.new
 def Joker.can_be_next_to?(other)
   true
 end

ジョーカーは、クラスとしては Object クラスのインスタンスです。 Card クラスとの関連は、継承では表現されていません。 しかし、それと関係なく can_be_next_to? というメソッドがジョーカーという特定のオブジェクトに定義されています。

このように、特異メソッドとは、クラス (に属する多数のオブジェクト) でなく、 特定の単体のオブジェクトに直接ふるまいを定義できる仕組みです。

静的な型システムの厳密性

上記の例では、 特異メソッド付きオブジェクトを使うことで、ジョーカーと一般カードの型の関係を明確にすることを避けています。 それをあいまいにしたまま、手札と場札の関係を漠然と考えて、 その漠然としたイメージのままモデリングして、それをそのまま実行しています。

Java や C# のような静的な型を持つ言語では、こういうあいまいなことは許されません。

静的な型を持つ言語では、全てのオブジェクトのふるまいは、クラスの中に定義されます。 従って、ジョーカーも一般カードと同様、クラスとして記述されます。 そして、ジョーカーをクラスとしてモデリングして行く過程で、 上記のサンプルコードの段階より一歩進めた分析が必要となります。

同じ例を Java で書いたものを見てください。

// (七並べ Java バージョン)
public interface ICard {
	boolean canBeNextTo(Card card) ;
}

public class Joker implements ICard {
public boolean canBeNextTo(Card card) {
	return true ;
}
}

public class Card implements ICard {
final static int SPADE= 1 ;
final static int CLUB = 2 ;
final static int HEART = 3 ;
final static int DIAMOND= 4 ;
Card(int suite, int number ) {
	this.suite = suite ;
	this.number = number ;
}
int getSuite() { return suite ; }
int getNumber() { return number ; }
public boolean canBeNextTo(Card other) {
	if (this.getSuite() == other.getSuite() &&
			( this.getNumber() == other.getNumber() + 1 ||
					this.getNumber() == other.getNumber() - 1)){
		return true ;
	} else {
		return false;
	}
}
int suite ;
int number ;

	public static void main(String[] args) {
	Card bafuda[]  = {
			new Card(CLUB, 7) ,
			new Card(SPADE, 7) ,
			new Card(HEART, 7) ,
			new Card(DIAMOND, 7) ,
	} ;
	ICard tefuda[] = {
			new Card(CLUB, 1),
			new Card(CLUB, 2),
			new Card(CLUB, 8),
			new Joker()
	} ;
	ArrayList ok  = new ArrayList() ;

		for(int i = 0; i < tefuda.length ; i++) {
		for(int j = 0 ; j < bafuda.length ; j++) {
			if (tefuda[i].canBeNextTo(bafuda[j])) {
				ok.add(tefuda[i]) ;
				break ;
			}
		}
	}
	System.out.println("手札の中に出せる札が" + ok.size() + "枚あります\n") ;
}
}

これは、前の Ruby の例をほぼそのまま Java に書きかえたものですが、 Java には特異メソッドという概念がありませんから、 ジョーカーを class として実装する必要があります。 そして、class として実装するには、 ジョーカーと一般カードの関係について、より詳細な分析が必要となります。 Ruby の例と比較すると、このコードでは以下のことが明示的に表現されています。

  • ジョーカーと一般カードの共通点が ICard インターフェースとして、相違点が、CCard クラス、Joker クラスとして明確に記述されている
  • 手札 (ICard の配列) にはジョーカーが含まれるが、場札 (CCard の配列) にはジョーカーが含まれない (場にはジョーカーを置くことはできない)
  • canBeNextTo() には一般カードしか与えることができない

こちらの方がモデルによって表現されている内容は多く、 これを動作可能にする過程で、さまざまな設計上の決断が行なわれています。 アプリケーションドメインの中のさまざまな概念の間の関係について、 より一歩進んだレベルで把握しないと、Java で動作するコードを書くことはできません。

そして、「把握」とは、 単一継承 + インターフェースという Java の概念に、 アプリケーションの概念を引き寄せて理解することを意味しています。 それによって、矛盾やあいまいさが排除されて明解になりますが、 プログラムを動作させる形にするために、より多くの負荷がかかります。

モデリングツールとしてのプログラミング言語

さて、一般の人が「七並べ」と言われてパッと思い浮かべるイメージに近いのはどちらでしょうか?

哺乳類というクラスから犬と猫のクラスが導出されるというような例ならば、 そのクラスの階層が頭の中のイメージに対応しているような気がします。 哺乳類に対して、犬と猫が同等の関係で導出クラスの位置を占めていることが自然です。 しかし、ジョーカーと一般カードの関係は、犬と猫との関係とは違うのではないでしょうか。 ジョーカーは単独の「特異」なカードであって、 一般カードのように自分の同類が多数存在するカードではありません。 Ruby のコードにあった「ジョーカーが『特異』なカードであるという雰囲気」が、Java のコードでは失われています。

ジョーカーというクラスを作ってそれを一般カードのクラスと関連づけるよりは、 唯一の特異なオブジェクトとして表現して、あくまでジョーカー独特の個別のふるまいを、特異メソッド表現していく方が、 何も考えない時に私の「七並べ」のイメージに近いと私は感じます。

Java にはクラスとインターフェースという概念しか無いのに対し、 Ruby にはそれらに加えて、「特異メソッド」という語彙がありますので、 クラスの階層に割りつけることが不自然になってしまうような、特殊な概念も、自然に表現することができるわけです。

つまり、モデリングツールとしての Ruby の特性は次の二点ではないかと思います。

  • 対象領域の世界にある概念を把握するための基本的な語彙が豊富である
  • 実装されるモデルの厳密化を遅延できる

カードゲームの例は、アプリケーションの世界がほぼ形式的に厳密に定義できる世界です。 ですから、モデルを厳密化することにはそれほど負担がかかりません。

しかし、実際の業務においては、対象領域の事物はもっとあいまいで複雑です。 そして、多くの場合、一般的なふるまいをクラスでモデリングしていくことは容易ですが、 カードゲームのジョーカーのような、そのような一元的な定式化を行うことが難しい要素がさまざまなレベルで出現します。

Ruby という言語は、 そのような特殊な要素を記述するための「語彙」を、この特異メソッドを始めとしてたくさん持っています。 それらの多くの「語彙」は、特定の世界観で緊密に統合されているわけではありませんが、 全く無秩序に乱雑に詰めこまれているわけでもありません。

その、適切に用意された「語彙」を活用できるようになると、 脳内に漠然と浮かんだ断片的なコード(のようなもの)を、そのまま Ruby のコードとして書き出せるようになります。 それはあいまいで矛盾を含んだものであり、 多くのシステムではさらなる分析と明確化を行なわないと、きちんと動作するプログラムにはなりません。

しかし、その脳内の丸コピーのような、いいかげんなコードが Ruby の場合はそのまま動くのです。 そして動かしながら、厳密化、定式化の方向性を探ることができるわけです。

外界の事物はトランプよりずっと複雑で、ずっと多くの矛盾を含んでいます。 その矛盾をそのままモデリングするのに便利な多くの語彙を、過剰でない絶妙な秩序の中に持っていることが、 Ruby のモデリングツールとしての大きなメリットだと思います。

実行可能手続きとしてのプログラム VS モデリングとしてのシステム

一方で別の見方をすれば、プログラムというものは、特定の入力に対して決まった手続きで特定の出力を行なうものです。 当然ながら、そのような観点も一方で必要であり、 そこから産まれる厳密性が必要とされる局面も数多くあります。 そういう観点からは、Ruby の私が長所と述べているものがそのまま Ruby の短所に見えるでしょう。

しかし、ソフトウェア工学の発展の歴史は、 ソフトウェアを単体の固定された静的な「プログラム」と見る観点から、 人間系も含め動的にメンテナンスされていく「システム」として見る観点に重点を移していく過程だと思います。

つまり、特定の定義された状況で特定の入力に期待される出力を行なうだけの機械的な手続きではなく、 世界のある側面(複雑性)を、なるべくその複雑性を維持したまま内部にモデルとして抱えこんでいて、 さまざまなレベルで起こる世界のダイナミックな変化に追随していけるシステムであることこそが、 ソフトウェアに求められている特性であるということです。

新聞の例に戻ると、新聞が世界に対して適切なモデルであり続けるためには、 世界を見るための語彙や枠組みが豊富であり、さまざまな観点を持った多様な記者を用意していることが必要です。 新聞が特定の枠組みで世界を見ていて、あらゆる事件を一定のトーンの社説で論じていたら、 その新聞というフィルターを通して見ると、全ての事件が均一に見えます。 自分のとっての事件の意味を正しく評価できなければ、読者は世界に適応できません。

正しく世界の複雑性をそのまま評価する社説は、 文字数や文体が一定であっても、事件の内容に応じてその文章のトーンが変化し、 それによって読者は、正しく事件の意味を評価できて、よりよく世界に適応できます。 Ruby という言語は、 そのような理想的な社説と同様に、適切な「複雑性の縮減」を行なえるだけの複雑性を備えているのです。

ここで取りあげた特異メソッドは、ほんの一例で、 こういうちょっとした要素が、Ruby という言語のさまざまな局面にあります。 それを、単に形式的整合性や統一感からの逸脱と見るか、 独特の美しさと見るかによって、Ruby の好き嫌いが分かれるような気がします。

ある瞬間の仕様を凍結して細かく定式化することは可能かもしれません。 しかし、それが将来においてどのように変化していくべきか、何が変化しない核の要素であるか、 というふうに時間軸の中でシステムを見ると、そこには多くの不確定性とあいまいさがあります。 多くの業務システムがインターネットと直結して、ドッグイヤーの中で世界全体との直接的競合にさらされている今、 その時間軸の中でのあいまいさは理論的、哲学的な課題ではなくて、目の前にある現実的な課題です。

この難題が、日々の業務の中で、ストレスとなって私の腸を痛めつけています。 それをわずかでも低減するために、私はさまざまなツールを使うわけですが、 その一つが「朝の儀式」の中で読む新聞であり、またもうひとつがRubyというプログラミング言語です。 「朝の儀式」で使う新聞が、いつまでこの「複雑性の縮減」という期待に答え続けてくれるかはわかりませんが、 Ruby というプログラミング言語は、これからずっと、これを助けてくれるものだと私は思っています。

終わりに

この文章で、私は「Ruby が絶対的に世界一の言語だ」とか言おうとしているものではありません。 たくさんあるプログラミング言語を比較するためのひとつの座標軸の提案です。 ここで提示した、新たな座標軸による比較が意味あるものであれば、 「いつ Ruby を使うべきか」「どういう分野で Ruby が生きるのか」ということと同時に、 「どういう場合に Ruby を避けるべきか」の見通しも、より明確につけられるものになります。

世の中に多数存在するプログラミング言語をどのように選択すべきかという「複雑性を縮減」し、 それぞれの言語が占めるべき位置をより明快に定位することに寄与できればうれしいと思います。

補足 (普通のやつらが普通のやつらの上を行く)

この記事において、私は静的な型を持つ言語の代表として Java を取りあげ、Ruby を比較しました。 同様のことは、C#、C++ 等の静的な型の言語全般に言えることだと思います。 補足として、「複雑性の縮減」という観点から、簡単に他の言語にも触れてみます。

まず、ここで書いたような意味での言語が内在する「複雑性」という特性を、最も多く共有する言語は Perl だと思います。 どちらも処理系のパーサが解読不能なくらい難しいのが、そのひとつの現れです。 いや、むしろこれこそが Ruby が Perl から最も大きく影響された所かもしれません。

先月号の Rubyist Hotlinks で、まつもとさんが「尊敬する人は?」という質問に、ラリー・ウォールと答えていますが、 その理由はここらへんにあるのではないかと、私は推測しています。

この文章で述べたことは、(実例を入れかえれば) そのまま Perl にも通じることで、 この観点からは、Ruby と Perl は、ほとんど同じような特性と見なせると私は思います。

次に、Ruby が最も「語彙の豊富な」言語であるかどうか。

これについては、ここで特異メソッドを例としてあげた「語彙」に相当するようなものを、 プログラマ自身が作り出すことのできる言語について考慮しなくてはなりません。 次の有名な文章は、Lisp についてこの「語彙」が創造できるメリットを強調しています。

普通のやつらの上を行け ---Beating the Averages--- http://www.shiro.dreamhost.com/scheme/trans/beating-the-averages-j.html

このような特性は、Ruby にも多少はありますが、明らかに Lisp 系の言語の方が勝っています。 特に、Scheme は、特異メソッドどころか、 オブジェクト指向の機能が完全にライブラリとして言語のコアと切離されていますから、 こういう機能をプログラマがカスタマイズして言語に追加することができます。 あるいは、継続を利用すれば、スレッド(並行処理)に関する機能さえも、 対象領域に合わせて、自由にカスタマイズして作りこむことができます。 Scheme においては、「語彙」を創造できるレベルが Ruby よりずっと深いと思います。

しかし、このような柔軟性は「普通」でない人にとっては強力なツールとなりますが、 「普通」のプログラマにとっては、ブっとび過ぎている面があります。 おそらく、Scheme のアプリケーションのコードは、ほとんど問題領域における記述と同一です。 「普通」のプログラマがそこに発見する複雑性は、やはり問題領域と同一であって、 それが前に見た Scheme と同じ言語で記述されていることが、理解できないのではないでしょうか。

Scheme を駆使して、「普通のやつらの上を行け」るのは、(少なくとも現状では)ごく一部のプログラマであって、 普通のプログラマが少しでも「普通のやつらの上を行け」るのは、Ruby になるような気がしています。

別の言い方をしてみましょう。 Scheme という言語は、 「普通のやつらの上を行け」るプログラマと言語をひとつのシステムとして把握した上で、 最も効果的な「複雑性の縮減」を行なえるような複雑性をそのシステムの内部に作り出すものと言えるかもしれません。 これに対して、Ruby や Perl は、「普通」のプログラマをシステムの中に取りこんで、 最も効果的な「複雑性の縮減」を行なおうとしているわけです。

補足2 ルーマンの社会システム理論について

この考察の中で私は、 新聞社、新聞の読者、情報システム、プログラミング言語を使うプログラマ等が、 最初から「システム」として自立した存在であるように扱い、 事前に設定されている所与の境界に対して「複雑性の縮減」を行なうような記述をしています。

ルーマンの社会システム理論は、 システムはそれ自身の固有の動作コードによる「複雑性の縮減」によって、自身と環境との境界を自己創出的に形成する、 という形で理論的に厳密な枠組みを与え、 それにより広汎な社会的事象に対する考察を可能とするものです。

ですから、厳密な意味でルーマンの理論を適用するとしたら、 プログラマというサブシステムがどのような固有コードで作動しているかを明確にした上で、 自己準拠性を取りこんだ考察をする必要があると思います。

また、ルーマンの理論においては、 社会システムの構成要素がルーマン独自の定義による独特の「コミュニケーション」という概念とされています。 プログラムの作成をシステム理論的にとらえるためには、 それをルーマンの「コミュニケーション」という概念に厳密にマッピングする必要があります。

ですから、ここで述べたようなあいまいな議論の中で、ルーマンに由来するものとして「複雑性の縮減」という言葉を使うのは、 純粋に学問的な観点からは、あまり好ましいことではないかもしれません。

それでもあえて、これを援用して Ruby という言語の魅力について述べてみようと思ったのは、 両者の間に本質的なつながりがあるように直感しているからです。

それはどちらも「意味」を対象としていることです。 「意味」というものは、経済学や物理学のような、演繹的、数理的な分析にはなじみません。 ルーマンの理論は、ただの主観的な印象論ではなく、数学や論理学に近い厳密さのある理論ですが、 「意味」で構成されたシステムを対象とし、 それに真正面から取り組んでいる所に、独特の魅力があります。

Ruby もやはり、コードの「意味」ということをいろいろな所で重要視している言語だと思いますが、 独特なアプローチによって「意味」についての一般的な解を見出した所に、社会システム理論とつながりがあるような気がしています。

謝辞

この文章の作成にあたっては、金田智之さんと、Rubyinst Magazine 編集部のすぎむしさんからたくさんの助言と示唆をいただきました。 お二人のご協力に感謝いたします。

参考文献

著者について

中島拓

(株)ブレーン http://www.brain-tokyo.jp/ でプログラマをやっています。 一応、amrita の開発者なんですが、最近ブログ書きに夢中でメンテナンスに手が回らず、やや顰蹙を買っています。 この記事をきっかけに、なんとかもう少し Ruby 方面に力を入れようと思っています。 連絡先は、tnakajima@brain-tokyo.jp です。