2012/07/06

自由なプログラム

こんにちは、このブログがウクライナやラトビアで大人気なようで有頂天になっている@tomoodaです。 いつもプログラミングの技術についてなんだかヘンテコな理屈をこね回してばかりなんで、今回は技術というより楽しさみたいな話をしてみます。

オレはブルックススキーなんで、ソフトウェアについて考える時にはいつもブルックスがソフトウェアの4つの本質的困難として掲げた複雑性や同調性、可変性、不可視性を基準として考えを纏めるようにしています。これらの本質的困難というのは、実はソフトウェアの楽しさの裏返しにもなっています。つまり、複雑性は複雑なものを組み上げて動かす楽しさ、同調性はレゴや機械をうまく制御したり人間と調和して対話する楽しさ、可変性は既にあるゲーム等を改造して遊ぶ楽しさ、そして不可視性は物体としての実体がないことの楽しさ。ん?物体としての実体がないと何が楽しいんだろう?もうちょっと掘り下げてみましょう。

ソフトウェア以外に、物体としての実体がないのにアレコレできて楽しいものって、どんなものがありますかね?1つは魔法の呪文です。世界中いろいろな所に魔法の物語があります。イギリス発の魔法学校の物語があったり、日本にも魔法少女のアニメがいっぱいありますね。あれって、なんで楽しいんでしょう?たぶん、物体としての実体がないからこそ、どんな魔法でも創造できちゃうんですね。だから楽しいんですよ。魔法学校の話のほうなんて、もう状況設定からして荒唐無稽で支離滅裂。そんな伝統社会の常識からの自由があの作品の面白さなんですよ。常識からの自由、物理的実体からの「自由」が魔法の呪文の根底にあると思う訳です。

昔、呪文が本気で信じられていた時代もありました。その頃は、呪文は「実体がないのに面白いもの」ではなく、まさに物理世界の根源と思われていたのだろうと思います。その時代の呪文は、実体がないという不可視性よりも森羅万象との同調性が重視されていたのではないでしょうか。話はちょっと横道に逸れてしまいましたが、ようするにソフトウェアの不可視性からくる楽しさというのは、魔法の呪文をつくるような楽しさがあるし、同時に魔法の呪文と同様に物理的な物体を動かしたり人間にイタズラできたりする。プログラマは現代の魔法使いなのだと思います。

不可視性の楽しさの根底にあるものを「物理的実体からの自由」と書きましたが、これこそオレがソフトウェアに惹かれた原点でもあります。小学生の頃、友達から「ハムやらない?」と誘われたのがそもそもの発端。そこから電子工作の楽しさに取り憑かれました。それまでは自宅の1Fの町工場で材料と工具を借りてベニア板とか鉄線とかで工作するのが趣味だったのが、ベニア板や鉄線では滅茶苦茶難しそうなことが、トランジスタやICを線でつなげるだけで実現できる。LEDが生き物みたいに光って動く。そして部品屋(浜松のマルツ電波)に入り浸るようになって、マルツの1Fでマイコンに出会ってしまった。MZ-80Bと書かれた小さなテレビで、ハンダ付けしなくても鳥がパタパタと羽ばたいている。何十行ぐらいの英語みたいのを入れると、動くらしい。まさにこの瞬間、この少年の将来の職業が確定しました。ツ

というわけでオレがプログラミングをはじめたのは「物理的実体からの自由」だったわけですが、ソフトウェアにまつわる自由は物理的実体だけじゃなくて、色々あるわけです。フリーソフトウェア、つまり政治的自由を求めるソフトウェアなんかは代表例ですね。フリーソフトウェア運動がプログラマから出てきているという歴史的経緯から言っても、ソフトウェアって、色々な意味で本来自由なものなんだろうな、ソフトウェア作りに身を投じている人は結構な割合で自由に惹かれてきたんだろうな、と思う訳です。

その一方で、静的型付けだとか、契約プログラミングだとか、形式手法だとか、プログラムに対する厳格な制約を適用する技術も沢山あります。これらは一見すると自由を制限するようにも思えるかもしれませんが、実際にはこれらは「この制約を守る限り、自由にやっていいよ」という自由裁量を確保するための技術とも言えます。つまり、「どうやって実現するのかという方法の自由」です。方法の抽象化と呼んでもいいかもしれません。指定された制約を守ればよいということは、つまり、「ここは、こうに決まってるだろ、業界の常識だぞ?」とか「実はお客さんにこういう提案しちゃったから、この実装じゃ駄目だよ。」みたいな暗黙の制約を排除して、プログラマの書きたいように書いて楽しむ事ができるわけです。自由を満喫しましょう。

では動的型付けはどうでしょう?もちろん、これもやはり自由のための技術という説明ができます。動的型付けが実現しようとする「極限までの遅延」とはつまり、「極限まで実装を選択する自由を残す」ということです。ある式を評価する時の具体的な実装コードを、まさに評価している最中にまで保留して、実装コードからくる制約をできるかぎり遅延させて、自由でいられる時間を引き延ばしています。動的型付けもやはり「どうやって実現するのかという方法の自由」を実現するための技術なのです。自由っていいですね。

最近ではVMで動作するプログラム処理系が増えてきました。これもやはり計算機械という物理的実体を抽象して自由を確保するための技術です。計算機械は、1つ1つの機械語のインストラクションが駆動します。プログラムを書くということは、計算機械に適切なタイミングで適切なインストラクションを供給することです。計算機械は物理法則で動いています。プログラミング言語の大切な機能は、この「計算機械」を抽象することです。物理法則で動く計算機械を抽象することで自由を確保することです。そしてVMというアーキテクチャはその抽象をアーキテクチャで実現しようというものです。

計算機械を仮想化することで物理的実体から自由になるという意味では、仮想化サーバやクラウドコンピューティングもやはり同様ですね。JS上でLinuxが動きました、なんて話を聞いてワクワク楽しい気分になるのは、やっぱりCPUやOSに縛られない、自由だからという要素もあるかもしれません。

もう、いっぱいありすぎて、1つ1つ挙げていったらキリがないです。でもこれは当然のことです。あのブルックスさんがソフトウェアの本質とまで言っていることなのだから。オレの狭い了見で書いたこの駄文だけではほんの氷山の一角です。他の3つの本質的困難についてもやっぱり沢山あると思います。複雑性のほうは、もうこれこそがソフトウェア工学の華といってもいい分野ですね。個人的には、ソフトウェア工学は複雑性に集中しすぎてないかな、という印象があって、でも最近はUIとかUXとかアジャイルとかTDDとか、他の3つの本質的困難=本質的楽しさに切り込んでいく分野も出てきていて、どんどん楽しくなってきました。

ブルックスさん自身が語るプログラミングの楽しさについては、この抜粋がわかりやすいです。さすがです。オレなんて、これをプリントアウトして会社のキャビネに貼ってます。どうです、オレってスイーツなブルックス信者でしょ?

で、結局何が言いたかったかというと、なんで「楽しさ」について書こうと思っていたのに、気付いたら技術のことばかり書いちゃうのかなー、ってことです。シ

2012/06/16

何も隠されてはいない

こんにちは、あまりにも更新が少ないんで、そろそろこのブログも存在を忘れられつつあるのではと憂慮している@tomoodaです。 今日のダベりネタは情報隠蔽です。

オブジェクト指向といえば情報隠蔽によるモジュール化という説明を見かけるたびに、「オブジェクト指向は何も隠してなんていないのに」と思ったりします。 以前、クラス/インスタンス関係と型/値関係をゆる~くしてみようで、オブジェクト指向は分類学じゃないよ、クラスはオブジェクトを分類するんじゃなくて、振る舞いを分類しているだけなんだよー、とか書いてみました。 まあオレが言うことだからあんまり信憑性ないかもしんないけど、情報隠蔽についてもなんだか誤解が多いなあと思ってます。

まずは、クラスはインスタンスの分類じゃなくて振る舞いの分類なんだよ、を振り返ってみましょう。

例えば、悪名高い、動物の例。動物クラスと犬クラスと猫クラスがあります。 これらのクラスは個々の動物を犬とか猫とか動物に分類するんじゃないんです。 個々の動物の色々な振る舞いを列挙してみましょう。 散歩したり、マーキングしたり、ブロック塀に上ったり、フリスビーをくわえたり。 これらの振る舞いを、これは犬の振る舞い、これは猫の振る舞い、これは動物一般の振る舞い、と分類するんです。 それがクラスだと思うのです。

単に振る舞いをまとめただけだから、分類学じみたことなんて必要ないです。実装上の便宜で適当に決めていけばいいです。 実装継承バンザイ。いいんですよ、それで。

こう書くと、多くの人は「クラスが定義するメソッド(メンバ関数)はそうかもしれないけど、インスタンス変数(メンバ変数)はどうすんだ?あれはオブジェクトの構造を定義しているんじゃないのか?クラスがインスタンスの構造を定義しているんなら、クラスはインスタンスを分類しているんじゃないか!」と思うかもしれません。

ブッポ〜ン

まあ正当派のソフトウェア工学では、クラスはオブジェクトの構造を定義すると教えています。 そしてその根拠が抽象データ型です。 抽象データ型は値が持つデータ構造を手続き抽象によって隠蔽するという機構ですが、多くのソフトウェア工学の本ではオブジェクトは抽象データ型の実装の1つであると見なしているようです。

でも、残念ながら、オレオレオブジェクト指向では、オブジェクトは抽象データ型の実装ではありません。 全くの別物です。 では、クラスで定義するインスタンス変数(メンバ変数)が、抽象データ型で隠蔽されるデータ構造ではないのなら、一体何なんなんでしょう。

それは「振る舞いを実現する環境」です。 ここで言う環境というのは、クロージャに近いです。ネストした内側のクロージャが外側のクロージャのローカル変数を参照するようなものです。 別にオブジェクトのデータ構造とかじゃなくて、オブジェクトの振る舞いを実現するメソッドを実装するための環境なんです。

そう考えると、クラスというのはオブジェクトの振る舞いを分類して定義しているだけの話で、分類学的な整合性とかどうでもいいし、データ構造を隠すとかはどうでも良い話なのです。 オブジェクトの本質は、ユーザが認識した対象を捕まえて、そいつにメッセージを送って、そいつがメッセージに対して何らかの振る舞いをすることです。 つまり、データ構造を隠すも何も、データなんて最初からオブジェクト自身にとってはどうでもいいことで、大事なのはその振る舞いなのです。

つまり、オブジェクトは何も隠してなんていません。 少なくともSmalltalkでは。

次に、Smalltalkでは何も隠されていないどころか、全てが表現されるという話をします。

Smalltalkでは全てがオブジェクトだと言われていますが、実は、Smalltalkでもっと大事なのは、全てが評価可能な式として表現されることなのです。 これこそがSmalltalkをSmalltalkたらしめている所以です。

Smalltalkの素晴らしさは、そのダイナミズムにあります。できるかぎりあらゆる事が動的に決まります。 アラン・ケイ博士がいうところの「あらゆる事の、極限までの遅延」です。 そして、Smalltalkでのダイナミズム、すなわちワークスペースでの表現式評価も、クラス定義も、メソッド定義も、全ては「評価可能な式」として表現されます。 Smalltalk内で発生したダイナミズムは「評価可能な式」として表現され、記録され、そして実行されます。 「あらゆる事の、極限までの遅延」があるから、記録されたダイナミズムは再実行が可能になります。 そして記録されたダイナミズムがSmalltalk環境の再構築を可能にし、記録されたダイナミズムを別の環境で再実行することを可能にし、さらなるダイナミズムを生み出します。

どうですか? 本来のオブジェクト指向では「何も隠されていない」のです。 なぜなら、プログラマに全てを曝け出し、全てを分解することができ、全てを変更することができることこそがオブジェクト指向が本来目指しているプログラミングだからです。

つまり、隠したいプログラムがあったらオブジェクト指向言語はお勧めしません、というのが今日の結論です。ツ

2012/04/11

再利用をめぐるあれやこれや

こんにちは、ずいぶん久しぶりの更新のテーマは、再利用性です。

再利用性はソフトウェア工学永遠のテーマです。 再利用性は生産性を向上させるための必須項目とされています。 多くのプログラミング言語やプログラミング環境が再利用性を謳い、それでいてその多くは話題から去っていきました。 たぶん、その言語や環境が提供する「再利用性」なるものが、多くの人にとって必要とされている「再利用性」とは異なっていたのでしょう。 その結果「もっと上流で再利用しないと、下流でだけ再利用しても効果がないよ」とか「それはツールがまだ揃ってないからだよ」とか、色々な枝葉が出てきています。 そこで、ふと思うのです。再利用って一体何なんだろう?再利用が向上させる生産性とは一体何なんだろう?

単純に考えれば、プログラミングにおける再利用性というのは、あるプログラムの構成要素を別のプログラムを構成するために流用することです。 関数であったり、述語であったり、オブジェクトであったり、クラスであったり、パッケージであったり、プログラムの断片だったり、色々な構成要素が再利用の対象になります。 ある関数を別のプログラムに流用する。簡単じゃん、どうしてみんなそうしないの?と思いますが、もちろん簡単じゃない理由があります。

全てのプログラムには、正常に動作するための条件が設定されています。CPUだったりOSだったりメモリサイズだったりネットワークだったり、色々な「環境」を前提にしています。 プログラムの構成要素にも前提条件があります。逆に言えば、前提条件に合致しない環境に流用しても正しく動作しません。 例えば、引数として整数を受け付ける関数に文字列を渡しても正常に動作しません。 静的型付け言語であれば型エラーや暗黙的型変換をするでしょう。動的型付言語だと動くかもしれませんし、動かないかもしれません。 この意味では、再利用性を高めるためには前提条件を少なくすることが必要だと言えます。 より正確に言えば再利用性を高めるためには前提条件を明確にすることが必要で、さらにその前提条件が少なければ嬉しい、ということになります。

その上で、前提条件を変更しやすいということが大事だと思っています。

単純な例として、ソートを挙げてみます。 整数の配列を引数として取って、その内容を小さい順に並べ直した配列を返す関数を定義してみます。 ここで再利用性を高めるために何が出来るでしょうか? 配列の要素の型を、整数だけでなく浮動小数点数や文字列など、他の型でも使えるようにすることも「前提条件の変更」です。 また、配列だけではなく、線形リストや木構造を引数として取る事が出来るようにすることも「前提条件の変更」です。 さらに引数が一度に与えられるのではなく、関数を評価している最中に引数の配列の要素が段々と埋まっていく、という環境でも使えるようにすることも「前提条件の変更」ですね。 もしかしたら、「大小関係」自体も最初から定義されているのではなく、ソート関数を評価している最中に1つ1つ例示しながら学習していく、というのも考えられます。 前提条件には、データ構造も制御構造もIOも問題定義自体も含まれます。 どれも変わるかもしれない前提条件なのです。

オレオレ哲学になっちゃいますが、世の中には通時的に必然的な正しさなど無いと思います。 何が正しいのかはその時やその場所によって移り変わっていく世界観や価値観で変わっていきます。 データ構造も制御構造もIOも問題定義も、どれも前提条件として変化していくものです。 一度書いたらあとは使うだけ、というのは幻想です。 書いたときには絶対に正しいと信じていた事が、後になって間違いに変化してしまうのが常です。

一度書いたら、次はもっと良いモノを書く。 それをやりやすいプログラミング言語、それをやりやすいプログラミング環境というのが、再利用性の高い言語や環境だと思います。

同じことが生産性にも言えます。 生産性とは何でしょうか? 多くの職業プログラマにとって生産性とは、単位時間に生産するコードの量でしょう。 同じ期間かけたらより多くのプログラムを書いた方が生産性が高いし、同じプログラムを書くのに必要な時間が短かくてもやはり生産性が高いと言われます。

では、再利用の視点で生産性が高いプログラマとは、過去に誰かが書いたコードを継ぎ接ぎしてコードを仕立て上げる能力が高いプログラマなんでしょうか? たぶんソフトウェア工学的にはそうなのでしょう。 でもそれってなんか違うと思うのですよ。 だって、プログラマは生ものですから。

オレには「ある問題を解決するためにプログラムを書いたら、次に問題を解決する時にはもっと良いプログラムを書ける」という信念があります。 逆に言えば、そうでなくなったらプログラマを引退すべきだと思ってます。 生産性が高いプログラマになるということは、大量のコードを書けるということではなくて、その問題を解決するためにより簡単に、より便利に、より納得がいく解決法としてのプログラムをサクッと書ける、ということだと思っています。 つまり、オレにとっての生産性とは、同じ時間でどれだけたくさんのコードを書けるかではなく、同じ時間でどれだけ賢くなれるか、ということです。

そうでも思わないと、別に賢くもないオレなんて、やってられませんよ。ツ

2011/12/17

AIについて考えると自動整形や形式手法が気になってくるでござるの巻

こんにちは、しばらくブログを放置していた@tomoodaです。 今日のお題はAIについてです。

コンピュータ関係でAIと言うと、普通は人工知能のことですね。Artificial Intelligenceの略。で、今回オレオレするのは、Augmenting Intelligenceのほうです。日本語にすると、知性増強ですかね。ようするに、人工知能のAIは人間の知性をコンピュータで再現するものだけれども、知性増強のほうのAIは人間の知性をどうやって増強していくかという話です。

まあ人工知能にしても、知性増強にしても、はっきりいって傲慢な話ですよ。機械のくせに人間様の知性をとやかく言うわけですから。でもね、近視や乱視の人がメガネやコンタクトレンズでモノをよく見えるようになるでしょ?あれと同じようなものです。元々人間ができることを、ちょっと弱点を補う道具を使うことで、もっと力を発揮できるようになろうよ、ということです。

じゃあ人間の知性の弱点って何でしょうね。まず1つ目は、短期記憶の貧弱さを取り上げてみましょう。

短期記憶というのはどういうものかというと、例えばこの文章を読んでいる時、あなたは今読んでいる→この単語←のいくつか前の単語を憶えているから、文章の係り受けや意味的な解釈ができるわけです。すごいですよね。人間の言語能力のかなり根幹に近いような機能です。でも、この短期記憶ってヤツ、実は結構ヘッポコなんですよ。記憶容量が狭い。色々な説がありますが、まあ5個とか9個とか、その程度の話です。この、テラバイトからペタバイトに進もうという時代に。

短期記憶が小さいとどんな不都合があるかというと、例えばLISPコードのような頻繁にネストが発生しているような構文を追いかけるのが難しいのです。LISPerはコツをつかんでまるで息をするようにカッコだらけのS式を読みこなしますが、訓練を積まないと読みこなすのは難しいです。

人間は文章を聞いたり読んだりして理解するとき、木構造ではなく線形に処理をしているのだろうと思います。じっさい、この文の構文構造を単語を葉とする二分木で表現してくださいと言ったら、10人に頼めば10通りの答えが返ってくるだろうと思います。つまり、木構造を持つ構文を正確に処理するのは難しいことです。

私は今はプログラミングを職業として生活しています。BNF等で形式的にキッチリ定義された木構造の文法でプログラミングします。使い慣れた言語ならば、単純な構文エラーは滅多に出しません。と思っています。だといいなあ。どうして「だといいなあ」なのかというと、プログラミング環境に助けられた上での話だからです。もし、紙とボールペンを渡されて「さあ10行ぐらいのメソッドを書け」と言われたら、Fizz Buzzぐらいなら何とかなるかもしれませんが、ちょっと頭を使うようなメソッドはまず間違えると思います。それも、アルゴリズムの構築ではなく、構文の部分で間違えるでしょう。

これがAugmenting Intelligence、知性増強の例です。プログラミング言語の形式的な定義に従って木構造を記述することに関する弱点を、コンピュータが構文を使った自動整形や色分け等で補っているわけです。

人間の弱点の2つ目として、表現の記憶が苦手である点も挙げておきます。これは、文章の意味を理解して憶えておくことは容易ですが、文面をそのまま字面通り記憶するのは難しいということです。電話で伝言を頼まれた時、その文面通りに伝えることは難しいですよね。

この欠点はシステム開発ではとても重大な問題をはらんでいます。つまり、表現を厳密に扱うことが出来ず、一旦自分なりに解釈したものを通して、他の関係者とやりとりをしているわけです。ようするに伝言ゲームなのですよ。仕様を伝言ゲームで決めたら、恐ろしいでしょう?でも、現状では多くの開発プロジェクトが実質的には伝言ゲームで開発しています。

それを何とかしようというのが、いわゆる形式的仕様記述ってヤツです。仕様を数学的な表現で書こうという話なんですが、多くの人はこれを「正しさを証明するってこと?」と認識しています。でも、形式的仕様記述のメリットは、証明だけではありません。伝言ゲームをストップする力があるのです。

形式的仕様記述言語は、構文も意味も形式的にキッチリ定義されています。だから、伝言ゲームによる「解釈の揺れ」が出にくくなっています。日本語のコトバを字面通りに正しくコピーするのは難しいですが、数式を字面通りに正しくコピーするのはそれほど難しくありません。なぜかというと、日本語に比べ、数式は表現式と意味が非常に近い関係にあるからじゃないかな、と思います。

もちろん、何でもかんでもシステム開発は形式的仕様記述しなきゃダメ、ってことじゃないです。何をつくるのか、つくりながら模索していくような開発の場合には、まだ決めるべきでない部分は決めないでおく、曖昧な部分を残す、ということも必要です。しかし、キッチリ書くべき部分を曖昧なままにしたがための不幸な結末なんてものもよく聞く話です。形式的仕様記述はそんな時に使うAI、知性増強の道具だと思っているのです。

というわけで、なんだかいつもにも増してダラダラと長い文章になってしまいました。このブログ自体、ツイッターの文の短さの反動から始めたものだし、すんませんが勘弁してやってください。ツ

2011/11/01

副作用礼賛

こんにちは、nil.htmlでひどい目にあった@tomoodaです。 今日は副作用についてまたまたオレオレ説を妄想してみます。

プログラミングの世界で副作用というと、多くの人は「破壊代入」「ファイルIO」「画面描画」「UIイベント」みたいなものを想像するでしょう。 そういえば破壊代入については以前もワケワカラン事を垂れ流した記憶があります。ツ

どうしてこれらを副作用と呼ぶのでしょうか?主作用というか効能みたいなのは何なんでしょうか? それは、「計算」ですね。 「計算」ですから、何らかの答えを得るために計算するわけですが、その答えを得ることが計算機の動作の主作用であり効能だ、という考えかたです。 この考え方は計算機科学あるいはソフトウェア工学の中で広く深く浸透しています。 でもねえ、それ、ホントかね?と思うのですよ。

計算機は文字通り計算する機械なわけですが、今ではちょっと名前的にミスマッチだと思います。 例えば自動販売機もコンピュータで制御されています。 もちろん、投入金額の計算とか、オツリの計算はちゃんとやってもらいたいですが、自動販売機の「主作用」は計算じゃないですよね。 オレにとって自動販売機の主作用は、オレにドクターペッパーを供給する、ということですよ。 できればダイエットドクターペッパーのほうがいいですが、普通のドクターペッパーでも勘弁してあげます。 ドクターペッパーをオレに供給した上でのカネ勘定だと思うわけです。

もうちょっと一般的に言えば、メディアないしはサービス装置としての計算機の主作用は、人に何かを見せたり、人から何か入力を受けつけたり、モノを動かしたり、そういう計算機科学がこれまで「副作用」と呼んできたものじゃないでしょうか。 人とのインタラクション、他の装置とのインタラクション、それこそが今の計算機にとっての主作用だとオレは思うのです。 足し算したり、掛け算したり、ループしたり、条件分岐したり、そういうのはインタラクションを実現するための手段であり、何ならやらなくて済むならやらないほうがいいのです。 だって、少ない足し算で同じインタラクションを実現できるのなら、少ない足し算のほうがいいに決まってますよ。

そう、オレから見れば、いわゆる計算機科学で扱う「計算」というものこそ、副作用なんです。

もちろん、計算なしには実現できないインタラクションこそが計算機の活躍する場だから、計算を全て排除とか言うつもりはありません。 計算は必要です。でも、少なければ少ないほどいい。 誤解を恐れずに言えば、今の計算機の使われ方では多くの場合、計算は必要悪なんじゃないかとすら思います。

そういうわけで、インタラクションの実現としてのプログラミングでは、計算を最小限に押さえてプログラムは「副作用」を表現する言語というのもあっていいと思います。 もちろん、「副作用」が絡まり合うととっても悲しいコードになってしまいます。 だから「副作用」をより合理的に効果的に記述することが必要なんだと思います。

副作用という点では、オブジェクト指向というのは利用者の認識と副作用を一致させるためのパラダイムだ、とも言えるような気がします。 以前、オブジェクト指向というのは、計算機の利用者が認識した対象に対してメッセージングすることだということを書きました。 「このメガネ」に対して「レンズはビビッドピンクがいいな」とメッセージを投げた時、眉毛がピンクになるのではなく、ちゃんとレンズがビビッドピンクになってほしいのです。 そして、ツルまでいっしょにピンクになったら、ツルもピンクになることが事前にわかったり、仕方ない時は事後でもいいからちゃんと利用者が認識できるようになったほしいのです。 つまりは、オブジェクト指向では、利用者が認識している対象を知ることも大事だし、何がどうなったのか対象を利用者に認識させることも大事なんじゃないかなー、と思うわけです。

だからといってオレの著者近影写真をフォトショで遊ばないでくださいね。ツ

2011/10/28

nilは悪か?

こんにちは、@tomoodaです。今回はnilのオレオレな勝手な話をタレ流します。

nil、言語によってはnullだったりNoneだったりもします。 ヌルポとか言われて嫌われています。 nullを発明したのはホーアさんですが、そのホーアさんまで「10億ドルの失敗」と言っているほどです。 そうなると、判官びいきなオレとしては、少しぐらいは弁明してあげたくなります。 ちなみにオレのミドルネームは善九郎で、九郎の部分は義経さんにあやかってだそうです。ツ

さて、nilは何のためにあるのでしょう? どんな時に使われるのでしょう? 多いのは、変数の初期値です。 nilを入れられる型の変数を初期化なしで宣言したらnilになる言語が多いですね。 また、失敗をあらわす返り値に使われることがあります。 便利じゃん?

じゃあ、nilの何が悪いのでしょうか?それは、期待外れだからです。 Vector型の変数で足し算しようとしたら、実はnilで足し算できずに例外が発生してしまうからです。 Image型の変数だから幅を調べようとしたらnilだからヌルポとか言われてしまうからです。 以前、型は値の集合であり、値が満たすべき性質をまとめたものだ、という話をしました。 まさにここでも同じことです。 Vector型の値は足し算ができるという性質を表明しています。 だから、Vector型の変数に足し算してみたくなったのです。 なのに足し算ができないなんて、詐欺じゃないか! Image型の値の横幅も同じです。 できると宣言されたことが、実際には出来ない。 だから皆が怒るのです。

nilが嫌われるもう1つの理由は、何もできないからです。 足し算もできないし、幅もありません。 SQLでは=で比較してもTRUE/FALSEで答えてもらえないのです。 ダメダメじゃん… 弁明の余地はないんでしょうか?

あります。ツ

nilがダメな第一の理由は、「期待外れだから」でした。 だから、「期待しなければいい」が解決策です。 期待外れだという例は、Vector型の変数なのに足し算できないから。 Image型の変数なのに幅がないから、でした。 え?Vector型?Image型? じゃあ、型が宣言されていなければいいんじゃん?

動的型付けの出番ですよ〜ツ

動的型付け言語のSmalltalkではどうでしょう? 変数の初期値はnilです。 nilは足し算できません。 だから何? 辞書とか、ソケットとか、配列とか、文字列とか、ビューとかコントローラとか、足し算できないオブジェクトなんて、他にも星の数ほどありますよー。ツ nilは幅を答えられません。 別に気にしなくていいよ。 整数とか、集合とか、時刻とか、幅がないオブジェクトなんて、他にも星の数ほどあるもんねー。ツ Smalltalkで問題になるのは、送りたいメッセージを受け取って、期待したことをしてくれるかどうかです。 nilかどうかなんて些細なことなんです。 VectorはVectorのできること、nilはnilのできることをすればいいのです。

じゃあSmalltalkのnilは何ができるのでしょう? 他にもデキナイ子がいるからといって、何もデキナくてもいいわけじゃありません。

nilはね、自分がnilだということを知っているのですよ。 nilであることを知っているから、nilかどうかという条件分岐ができます。 nilに、ifNil:というメッセージをクロージャといっしょに渡すことができます。 ifNil:というメッセージは、もしメッセージを受けたのがnilなら引数として渡したクロージャを評価する、というものです。 でも、nilのifNil:というメソッドには条件分岐なんか書いてありません。 だって、自分はnilだと自覚してますから。 無条件に実行しちゃえば、結果として条件分岐できちゃうのですよ。 すごいでしょ?条件分岐しなくても条件分岐できちゃう。計算能力が湧いて出てきちゃうのですよ。 これが、覚醒したnilの能力(チカラ)なのです。ツ

他にも、自分はNumberじゃないと知っていたりとか、自分の名前を文字列にしたりとか、それをストリームに書き出したりとか、意外と多芸なのですよ。 そしてこれが2番目の問題点の「何もできない」への回答です。

Smalltalkのnilは、UndefinedObjectというクラスの、立派なファーストクラスなオブジェクトです。 UndefinedObjectはObjectのサブクラスだから、Objectよりも賢いのですよ。 オブジェクト指向言語でオブジェクトより賢いなんて、王者の風格じゃないですか! ツ

というわけで、nilは確かに厄介なヤツですが、その厄介さがどこから出てきているかと言うと、実は静的型付けと、その言語におけるnilの役割に関する事柄だという話をしました。 念のために書いておきますが、静的型付けが悪いなんて言ってません。 型がした約束を免除されるnilと静的型付けの相性が悪いのです。 型がした約束を免除されないnilは静的型付けでも相性いいですよ。 HaskellのMaybeモナドのNothingはハズレ的な使われ方の時はnil的な感じですが、型の約束をちゃんと守っているから害はありません。 同様に、動的型付けなら約束を破る必要がハナからないので、やはり害はありません。 いや、ありませんは言いすぎかな? ちょっとあるかも。 ま、いいじゃん。ツ

というわけで、皆の嫌われ者のnilだけど、害がないように使ってあげてくださいね。ツ

2011/10/15

クラス/インスタンス関係と型/値関係をゆる~くしてみよう

こんにちは、ページビューが二進数表現でオール1になってご機嫌な@tomoodaです。ツ 今回はクラスと型について、オレオレ定義を垂れ流してみます。

あらかじめお断りしておきますが、このエントリに書いてあることは一般に認められた用語定義とは異なっています。 特に学生さん、テストでこのエントリに書いてあることを答えたら、講義をこれっぽっちも聞いてないことがバレてしまうから、要注意ですよ!ツ

基本的にオレはオブジェクト指向な人間で、関数型言語とかは昔やってたけど今はド素人なので、プログラミング言語を網羅している訳ではありません。 しかも、そのオブジェクト指向というのも、一般に知られているオブジェクト指向というよりオレオレOOなので、あんまり頭から信じ込まないでくださいね。 そういう考え方もあるのかもねー、ぐらいな感じでよろしく。

さて、オブジェクト指向というと、一般の入門書とかでは、まずクラスというオブジェクトの分類があると説明すると思います。 オレはそこがマズいと思ってて、それでオブジェクト指向=分類という先入観を持たれてしまう。 クラスがオブジェクトを分類するなんて、オレは全然そうは思わないのです。 もちろん、クラスという言葉は分類を意味しています。つまり、クラスとは分類。

なーんだ、やっぱオブジェクト指向は分類じゃん、と思うかもしれません。 クラスは分類なんだけど、何を分類しているかが問題なのですよ。 オレは、クラスは「振る舞い」を分類するものだと思うのです。 そう、クラスはインスタンスを分類するものではないと思うのですよ。 オブジェクトを分類するのではないから、クラスはオブジェクト指向の本質ではない、と思うわけです。 そして、クラスがオブジェクト指向の本質でない以上、オブジェクト指向は分類学ではない、ということになります。

システムの中には色々なオブジェクトがいます。 それらのオブジェクトに色々な「振る舞い」が定義されます。 クラスは、その「振る舞い」を分類するものなんです。

例えば、悪名高い、動物の例。動物クラスと犬クラスと猫クラスがあります。 これらのクラスは個々の動物を犬とか猫とか動物に分類するんじゃないんです。 個々の動物の色々な振る舞いを列挙してみましょう。 散歩したり、マーキングしたり、ブロック塀に上ったり、フリスビーをくわえたり。 これらの振る舞いを、これは犬の振る舞い、これは猫の振る舞い、これは動物一般の振る舞い、と分類するんです。 それがクラスだと思うのです。

単に振る舞いをまとめただけだから、分類学じみたことなんて必要ないです。実装上の便宜で適当に決めていけばいいです。 実装継承バンザイ。いいんですよ、それで。

でも、Javaとかにはクラス型ってあるでしょ?メソッドの引数が犬クラスだったら犬しか受け取らないわけだけど、これって、オブジェクトを分類してるんじゃないの?と思うかもしれません。 はい、クラス型はオブジェクトを分類します。 でも、それは本来、クラス型の「クラス」部分が分類してるんじゃないんです。 クラス型の「型」部分が分類しているんです。

型とは何か、というのは色々な角度から色々な定義があります。 ある時は、値の集合と定義されます。またある時は、ビット列の解釈だったりもします。また、関数との関係の中で型を定義する考え方もあったりします。 また、抽象データ型のように、情報のアクセス管理という側面もあります。 ここでは、型はとりあえず値の集合ということで、話を進めていきます。

型が値の集合なら、型とは、どのような値がその要素として含まれるのか、その条件こそが型の定義ということになります。 その条件の中には「偶数」みたいな条件もあるかもしれません。 「1から10までの整数」ってのもアリでしょうね。これはPASCAL等で実装されている、範囲型と呼ばれるものですね。 また、「スロットが2つある」というように構造に関する条件をつけることもあるでしょう。 さらに、「散歩する」というような振る舞いに関する条件もあるでしょう。これはJavaで言えばインターフェイス型みたいな感じですね。

もっと単純に、「犬クラスのインスタンス」という条件とかも考えられますねえ。これがクラス型の正体です。 さらに言うと、Javaのインターフェイス型は、振る舞いに関する条件を定義するのですが、直接この条件で値を分類しているのではありません。 この条件を満たす「クラス」が、「オレはこのインターフェイスを実装しているぞー!」と宣言をし、かつ、その宣言通りかチェックされ、そのチェックに合格したクラスのインスタンスであれば、そのインターフェイス型の値として認められます。 言ってみれば、「このインターフェイスを実装していると自他ともに認めたクラスのインスタンス」という条件です。 つまり、結局はクラス型による分類をしているということです。

では型は値を分類するものだとして、何のために分類するのでしょうか? それは、意味論的に正しいプログラムを記述するためです。 より正確に言えば、意図した通りに動作するプログラムを記述するために、プログラムがどのように振る舞うかを理解できるような仕組みが必要で、そのためにはプログラムの中で使われる様々な値がどのような性質を満たすのかを理解することが必要です。

型は、値に関する性質を理解可能なものにするための、骨組みとなるものです。 値に型がつけられているから、その値の性質がわかります。 そして、プログラムがその型が決めている性質を守るかどうかを確かめるための判断材料になるのです。 静的型付けは、プログラムを動かす前から(つまり静的に)値に型を結びつけることで、どのような振る舞いをするか、実行する前から理解できるようにするための技術と言えます。 一方で動的型付けは、プログラムを動かしている時になってはじめて(つまり動的に)値に型を結びつけることで、事前にかかる拘束を最小限にしながら、プログラムが動いている最中にその性質を理解できるようにするための技術と言えます。

プログラムが正しく動作するために値の満たすべき条件をまとめるのが型だとすると、当然、全体を見通した分類が必要です。どのような値がありえて、どのような値が望ましいか。どういう条件をつけたら正しく動作するのか。どういう条件を設定すれば拡張しやすくなるか。保守が楽になるのか。 分類学とは違いますが、型をしっかり考えて定義することが大切だと思います。 もちろん、実装継承はやめておいた方が良さそうですね。ツ

こうしてクラスと型を見比べてみると、全然別物だということが理解いただけるでしょうか? 振る舞いの分類と、値が満たすべき性質。 クラスは振る舞いの塊で、振る舞いを系統的なものにします。 型は述語の塊で、表明を系統的なものにします。 表明は振る舞いの仕様であり、振る舞いは表明の実装ですが、これら2つを1対1に結びつけるのではなく、 1つの型を複数のクラスで実装してもいいですし、1つのクラスの仕様を複数の型で規定してもいいでしょう。 表明のまとめかたと、振る舞いのまとめかた、これらは本来別々でもいいんじゃないでしょうかね?

そう考えると、クラス型だけでなく、動的型=クラスという動的型付けオブジェクト指向言語も、やっぱり変だと思います。 型とクラスとはもっとゆる〜い関係なほうがいいと思うのですよ。 それに、動的型、つまり値と型を固定的に束縛するのではなく、もっと動的、ダイナミックに対応付けたら面白いと思うのです。 どうせ型は表明の塊なら、色々な粒度の型を色々と定義して、実行時にある式の値がどの型の条件を満たすのか、それを定義してみると面白いかもしれません。

ってなものが、オレの思うクラスと型の関係です。うまくいけばそのうち、クラスではない、動的で型と値の関係をゆる~く結びつけるような「動的型」をPGeneに実装して、公開する日が来るかもしれません。ツ

2011/10/04

ピジンなプログラミング言語

みなさん、こんにちは。 ページビューがSmalltalkコードの行数に反比例することを発見してガッカリしている@tomoodaです。ツ

今日はSmalltalkコードの行数を増やす(つまりページビューを下げる)元凶となっているPGeneについて、くっちゃべります。

ピジン言語というものをご存知でしょうか? 異なる言語を話す人同士で会話するために生まれた混成語をピジン言語と呼びます。 そしてピジン言語がそのまま定着して、その土地で母語として話されるようになると、クレオール語と呼ばれます。 出世魚みたいでおもしろいですね。 例えば有名なのは、ハワイのピジン英語です。 ハワイのピジン英語はすっかりクレオール化されています。 また、最近世界遺産に登録された小笠原諸島でも、日本語と英語のチャンポンが話されていたということです。

オレ的には、そういう異文化接触的なものにロマンを感じていて、X言語のAをY言語で実装してみました、的なものは好きなほうです。 その遊び場として、PGene(Programs' Gene、発音はピジン)というパッケージを書いています。

ええ、その通り、PGeneという名前はプログラミング言語のフィーチャー(構成要素、言語仕様上の機能)を遺伝子的にみなして遺伝子を交換してみる、という意味と、ピジン言語の駄洒落です。 以前このブログでも書いた、Squeak / PharoでPython風ジェネレータ、というのも、この流れのお遊びです。 Pharoも1.3がリリースされたことだし、PGeneもそのうち後悔^H^H公開するつもりなので、よろしく。ツ

プログラミング言語の遺伝子と言うと、プログラミング言語の系統樹がありますね。 これには主な影響を与えた言語の木が記載されますが、プログラミング言語の遺伝子交換はこれだけではありません。 言語設計で複数の言語からフィーチャーを取り入れるだけでなく、その言語上のプログラムによっても遺伝子交換は行なわれていると思っています。

というのも、言語というのは、その言語仕様そのものよりも、その言語が実現しようとしている未来像にこそ意味があると思っているし、言語のフィーチャーについても同じく、未来像が大事だと思っているからです。 たとえば、オブジェクト指向は1つのフィーチャーであるかのように思われていますが、オブジェクト指向って何?で書いたように、各言語での「オブジェクト型」の仕様そのものより、「オブジェクト指向が目指している未来」が大事だと思うわけです。 同様に、Haskellのモナドもそうだし、リアクティブプログラミングも同じだと思っています。 静的型付けと動的型付けについても同じで、辞書的な定義よりも、目指す未来で語ってみたいねえと思ってエントリを書きました。

その意味で、ある言語Xで実装されたライブラリAを言語Yに移植しました、というのも遺伝子交換だと思います。 なぜなら、言語Y上のライブラリAには、原実装言語である言語Xが目指した「未来」が反映されているからです。 例えば、SmalltalkのMVCフレームワークは、勢い余って別の名前に替えられたものも含めて、多くの言語で再実装されています。 それら各言語流のMVCフレームワークには、Smalltalker達が見た、「インタラクティブなメディアとしてのコンピュータが実現するであろう未来」も一緒に移植されているはずなのです。 そう、移植は言語のトランスレーションではありません。夢と希望を別の言語で表現することなのですよ。

Smalltalk自体もSmalltalk-80以前のバージョンの進化がありますが、多くのプログラミング言語からフィーチャーを取り入れています。 クラスという概念はSimulaからでしょうし、動的環境としてはLISPの影響が多大でしょう。もちろん、LOGO抜きにSmalltalkは語れません。 そして、Smalltalkという言語の骨格の上に実に多くの概念が発生し、他の言語に移植され、また、他の言語から取り入れてきました。 他の言語に移植された例としてはMVCその他多くのデザインパターンがあり、他の言語からはトレイトなどを取り入れて、現在なお進化を続けています。

そんな風に、色々な言語の遺伝子を交換するのが好きなのですが、その一方で、1つの言語を設計する時にあれもこれもと寄せ集めるのは好きじゃありません。 色々な言語から「あれもいいな、これもいいな、あ、これも便利そうだ!」とばかりにフィーチャーを寄せ集めてしまうと、その言語が目指す「未来」がゴタゴタしてしまい、なんとも興醒めに思えてならないのです。

プログラミング言語というのは不思議な獣で、2つのフィーチャーを組み合わせると、それぞれ単独の2倍以上のスゴさを発揮することもあれば、クソになることも多々あります。 そのクソになった組み合せにまた別のフィーチャーを足すとあら不思議、すごく便利になっちゃいました、というのもあったりしますね。 例えば、Modula-3のobject型とbrandingだけだと、使えねえよそれ、って感じなのが、opaque typeを加えると、なんだか光り輝いたりします。

そんなにフィーチャーのごった煮が嫌いなら、何でPGeneなんて面白がって作るんだい?と不思議に思われるでしょう。 答えは、PGeneが目指しているのは、ピジンだからです。

プログラマは言語をモノにすると、そのプログラミング言語で考え、読み書きをします。 発想がプログラミング言語の中から湧き出てくるようになります。 もちろん、プログラミング言語は母語には絶対になりません。せいぜい頑張っても、第二言語どまりです。 プログラミング言語自体が、自然言語とのピジンとも言えるでしょう。

PGeneがいくらPharo/Squeak上にPython風ジェネレータを実装したところで、それは到底母語的な、発想の源にはなりません。 それ自体がピジンであるプログラミング言語のそのまたピジンにしかならないのです。 これはPGeneに限らず、あらゆるプログラムについても言えると思っています。 だからベースとなるプログラミング言語は、できるだけシンプルなものがよいと思っています。 オレ的には、言語仕様はミニマリストなアプローチがよく、 その言語の話者であるプログラマはそのミニマルな言語仕様の上で多いに多様なプログラミングをすればよいと思っています。

Smalltalkは言語仕様はとてもシンプルです。 そして、現在ではかなり古典的な言語になりました。 Smalltalkerは過去の栄光ばかり話していると揶揄されるようにもなりました。ある意味光栄なことではありますが。ツ

しかし先ほど書いたように、Smalltalkはまだまだ進化し続けています。 ミニマルな言語なだけに、どんどん現代的なフィーチャーを取り入れることができます。 オレはそんなSmalltalkが大好きだし、そんなSmalltalkを越えた向こうのほうにある未来を覗き見るのが大好きなのです。ツ

2011/09/24

プログラミングと名前

こんにちは、今回はプログラミングにおける名前の役割について、いつもの調子でクッチャベります。ツ

プログラミングでは、どんな場面で名前が使われるでしょうか? 変数名、関数名、クラス名などなど、色々な識別子という名前をつけます。 また、1や"abc"等のリテラルも名前と言えます。 もちろんリテラルにはラムダ式等のクロージャリテラルも含まれます。

さらに、表現式もその評価結果の値に対する名前ですね。例えば、1+2という表現式は、3という値の名前です。 特に、値への直接的な参照を使わずに、手続きを介した表現式で名前付けることを手続き抽象と呼んだりします。

さらに、プログラミングではプログラムの構成物だけでなく、外部リソースを使うことがよくあります。 外部リソースもファイル名、ファイルデスクリプタ、ホスト名、IPアドレス、ポート番号、プロセスID、ユーザID、等々、実に色々な名前が使われます。

こうして見ると、プログラミングは本当に色々な名前を付け、いろいろな名前を呼びますね。 オレ的には、プログラミングというのは結局のところ、いろいろなものに命名するということで、 プログラムを実行するというのは、そのいろいろなものの名前解決をするということじゃないか、と思うわけです。 以前オブジェクト指向って何?で書いたことも、実はユーザが認識している対象に名前を付けること、その名前解決をすること、と言うこともできちゃいます。

プログラミングで使われるこんなに色々な名前の中でも、識別子はその基礎になるものなので、識別子として適切な名前を付けることはとても重要だと考えられています。 そんなわけで、色々な場面で「名前付けのルール」が決められているわけです。

昔、計算機のパワーがまだまだ貧弱だった頃、プログラミング言語の識別子名は言語仕様で強い制約が課せられていました。 あなたも見たことがあるでしょうか、G000123みたいな識別子名。 これ、噂によると、123番目に承認されたグローバル変数、ということらしいです。 すごいですね。ツ ありがたいことに、ここまで極端な管理はもう少なくなりました。

それでも、今も色々な制約が残っています。 「ASCII文字しかダメー!」とかいう処理系もまだまだ残っているでしょう? アメリカ帝国主義には、ほんと溜息が出ます。ツ 識別子名の長さも、以前の「8文字まで」ほどクソじゃないですが、制限が残っている処理系もあったりします。

それでも最近は昔に比べればずいぶんと自由に名前付けができるようになり、そのためフリーダムすぎる状況を避けるために、コーディング規約としていわゆる命名規約なるものを導入することが多くなりました。 曰く、「fooとかhogeとか、意味のない名前を使うな!」とか。 ちなみにオレの友人は全ての変数名をblah, blee, ...という名前にしていました。ツ また、変数名は3文字以下にするな、とかも、よくある規約ですね。 オレも昔は、そうしたほうがいいと盲目的に信じてましたよ。 ホント、「変数名はちゃんと意味のある英語名にすること(キリッ!」と言ってたのですよ。

でも、今は違います。(キリリッ! ここ5年ぐらいで気付いたのですよ。「ちゃんと意味のある英語名」の弊害に。

意味を確定させた名前を付けないほうがよい場合の1つ目は、まだ意味を確定できない時です。 まだアイデアをスケッチしている段階で、どう呼んだらいいか、まだ確信を持てない場合。 とりあえず仮の名前を付ける時、下手に意味のある名前を仮に付けてしまうと、その名前に縛られます。

ジブリ映画に千と千尋の神隠しという作品がありますね。 皆さんはもう観ましたか? 劇中に、千尋の名前から「尋」を取り払って「セン」と呼ぶことで魂を支配しようとする描写がありました。 あれは実は逆に見ると、清浄な油屋の世界の皆さんがセンのことを、穢れた世界の千尋ではなく、清浄な世界のセンだと信じさせるための名前でもあると思うのです。 つまり、魂を縛る相手はセンだけでなく、油屋の皆さん。 名前は、その名前で呼ばれる側だけでなく、呼ぶ側の心も縛るのです。

だから、まだ意味を確定できない時には、意味のない名前で呼んだらいいと思います。 オノマトペ(擬声語)なんかがいいでしょう。 オノマトペは自然言語処理の分野でも盛んに研究されていますが、プログラミングに取り入れていくのも面白いんじゃないかと思います。 プログラムも結局のところ、人間のコトバなのだから、どんどん色々な種類のコトバを取り入れましょうよ。ツ

意味を確定させた名前を付けないほうがよい場合の2つ目は、認知負荷をかけたくない時です。 例えば、数学ではf(x)をaFunction(theFirstArgument)なんて書いたりしませんね。ツ 数学の世界では、fと言えばまあ関数だし、aFunction(theFirstArgument)なんて書かれても、読むのが大変なだけ。 見通しの点から言っても、f(x)のほうがスンナリ理解できます。

同様に、プログラミングの世界でも、i, j, kだとか、f, g, hとか、x, xsとか、1文字で書いたほうが読み易く、かつ、高い抽象度を確保できる「英語として意味を持たない短い変数名」は色々あります。 長くて意味あり気な名前を付けて、読むための負荷を高くし、ひどい時には抽象度を誤ってしまうような失敗は避けたいものです。

というわけで、プログラミングでは本質的に名前が担う役割は非常に大きいものです。 適切な名前を付けることは、楽しいプログラミング、愉快痛快なプログラミングに必要な作業です。 だから、名前を付ける時には、しっかり考えましょう。 意味を持たせる時には、しっかり意味を持たせましょう。 意味を持たせないほうがよい時には、しっかり意味を取り除きましょう。 さもないと、千尋が本名を思い出して、油屋が大変なことになりますよ。ツ

2011/09/23

破壊的代入と名前束縛は混ぜるな危険

みなさんこんにちは、@tomoodaです。 おかげさまでこのブログも前3記事へのページビューが1/2Kを越えました。 思った以上に読んでもらえて、嬉しいかぎりです。

さて、今回のテーマは、手続き型ベースのプログラミング言語での「代入」についてクッチャベります。ツ

変数という概念を数学で学んだ時には、「代入」とは、書き換えでした。 例えば、f(x) = a * x + bという式があった時、a=2, b = 3を「代入」すると、それぞれ前記の式の中の全てのaを2、bを3に「書き換え」て、f(x) = 2 * x + 3、としていましたね。

しかし、プログラミング言語、特に手続き型ベースのプログラミング言語では、代入は2種類あります。 それが破壊的代入と名前束縛です。

破壊的代入とは、ありていに言えば、代入文です。 f(x) = a * x + bについてaに2, bに3を代入して計算を進めていきながら、ある所でaやbに別の値を代入したりします。 同じ式f(0)を計算しても、aに2, bに3が代入された時の値と、aもbも0が代入された時とでは、f(0)の値が違ってしまいます。 そんなこともあって、いわゆる関数型言語など、破壊的代入は悪だとするプログラミングの流儀もあります。

数学でも同じ変数に何度も代入することがあります。1つは、関数適用です。 f(4)を計算した後でf(5)を計算することは、よくありますね。 xに代入する値を変えているわけです。 では、これは破壊的代入でしょうか?そんなことはありませんね。 xに4を代入した結果を計算している途中で更にxに5を代入し直すわけではありません。 xに4を代入するのとは別に、元の式に対して、xに5を代入しているわけです。 1つのコンテキストの中で書き換えるのではなく、別々のコンテキストの中で書き換えています。 これは名前束縛で、xという変数を4に束縛する、5に束縛する、と言います。

では、破壊的代入は悪なのでしょうか?撲滅すべき存在でしょうか? 薬の副作用と同じ意味で、破壊的代入を副作用の1つと見做して撲滅すべきだという主張もあります。 そう思う人達の言い分はもっともだし、撲滅したらしたでメリットはあるでしょう。 実際、オレも関数型言語は大好きです。 それでも、オレ的には撲滅すべきだと断ずるつもりは毛頭ありません。ツ

完全に副作用のない世界を追求するのも大事ですが、それ以上に、副作用の「実害を減らす」ことが大事だと思っているのですよ。

では、実害を減らすにはどうしましょうか? それにはまず、「対象を認識する」ことです。 そう、オブジェクト指向って何?で挙げた、一番大事なことです。 この場合、「対象を認識する」とは、破壊的代入と名前束縛をきちんと区別することです。 もう少し言えば、手続きの中で本質的に破壊的代入としてきちんと表現したい「代入」と、一時的に名前をつけておくために「代入」している本来「名前束縛」であるものを、表現としてきちんと書き分け、読み分ける、ということです。

そこで、手続き型ベースのオブジェクト指向言語のSmalltalkでやってみましょう。ツ 処理系としてはPharoを使います。 やることは簡単、2つのメソッドを追加するだけです。

まずは、Objectクラスに以下のメソッドを定義します。

=>> aBlock
    ^ aBlock value: self
さらに、Arrayクラスに
=>=> aBlock
    ^ aBlock valueWithArguments: self
も定義してみましょう。 たったこれだけで、破壊的代入と名前束縛を分離して、コードの見通しがスッキリします。

例えば、ネットワークプロトコルの単体テストのsetUpを書いてみます。 単体テストの対象はMyProtocolクラスで、単体テストをMyProtocolTestクラスに記述します。 MyProtocolTestクラスにはインスタンス変数としてpeer1, peer2が宣言されているとします。 また、テスト用にTCPコネクションの両端をシミュレートするクラスMyTestTCPConnectionがあるとします。

まずは、使用前。

setUp
    | connection stream |
    connection := MyTestTCPConnection new
    stream := connection stream1.
    peer1 := MyProtocol on: stream.
    stream := connection stream2.
    peer2 := MyProtocol on: stream.

では、使用後。

setUp
    MyTestTCPConnection new
        =>> [ :connection | 
            connection stream1
                =>> [ :stream1 |
                    peer1 := MyProtocol on: stream1 ].
            connection stream2
                =>> [ :stream2 |
                    peer2 := MyProtocol on: stream2 ] ]
さらに、こんな書き方も。
setUp
    MyTestTCPConnection new
        =>> [ :connection | 
            {(connection stream1).
            (connection stream2)}
                =>=> [ :stream1 :stream2 |
                    peer1 := MyProtocol on: stream1.
                    peer2 := MyProtocol on: stream2 ] ]

どうでしょう? 名前束縛のシンボル「=>>」と「=>=>」と、代入のシンボル「 :=」を区別すると、こんなにプログラムの構造が整理されます。

元々Pharoには=>>と全く同じ働きをするin:メッセージがありましたが、ビジュアルな効果を期待して、あえてシンボルで定義してみました。 プログラムの構造、名前束縛という「対象を認識する」ということを大事にする、それがオブジェクト指向プログラミングなのです。ツ

たった4行のプログラミングであたかも言語仕様が拡張されたかのような、視覚/意味/スタイルへの影響を誘導できる。 Smalltalkって本当に面白いですね! ツ

2011/09/18

では、静的型付けと動的型付けについて語りましょう

このブログを始めて3日目になりました。 おかげ様で、色々な皆さんに読んでいただけているようで、とても嬉しいです。 本当は、呑気に気が向いた時にポツリポツリと書いていこうと思っていたのですが、 折角なので、型付けについて書いてみようと思います。

結論から書きましょう。
プログラマにとって、静的型付けと動的型付けの違いは、問題領域を分割するか共有するかの違いです。

動的型付け言語の例として、Smalltalkを取り上げます。はい、そうです、Smalltalkが好きだからです。ツ

SmalltalkにはNumberクラスがあります。そう、数のことです。1とか3.14とか、1/2とかです。 もちろん、足し算とか掛け算とか平方根とかができる、賢い子です。 しかもその上、自分を表わす文字列まで作れちゃいます。 数だけでない、Stringクラス君に関わることまでできちゃうなんて、これはもう天才の領域でしょう。ツ でもね、それは「1」君のすごさの、ほんの、ほーんの一部なんです。

なんと、この「1」君、isStringなんてメッセージまで受け付けられるのです。 もしこのisStringメッセージを文字列が受け取ったら、trueを返します。

1がisStringメッセージを受け取ることができるということは、会社の仕事で言うと、「ねえねえ、君、X部長のところの人かな?」と言われても、X部長が誰のことか知らなくてもパニクったりせずに平然と「いいえ」と言えるだけの社会性を備えているんです。 すごいぜ1、既にオレを越えてやがる。ツ

では、静的型付け言語ではどうでしょうか? 1がisStringかなんて、訊くほうがバカでしょう。ツ だって、1は整数で、文字列なわけがない。 型エラー。 isStringとか訊く前に名札読め。それが出来ないような社会性のない馬鹿の書いたコードなんかコンパイルせんぞ! まさに正論ですね。 会社というのは、ある程度の規模からは、大抵はそういう風に出来ています。 つまり、社員一人一人には職掌があり、それぞれ裁量と責任が規則で定められているから、それに応じて仕事を分担し、連携して、組織として業務をしていきます。

これが、静的型付けでは問題領域を分割する、と言った意味です。 では、動的型付けではどうでしょう?

動的型付けの場合、先に書いた「1 isString」には重要な意味があります。 isStringじゃつまらない、とおっしゃる貴方、何ならifStringDo:なんてメソッドを定義してもいいですよ。 引数として「1引数のクロージャ」を取って、もし自分がStringだと思ったら、自身を引数にしてクロージャを評価します。

ここで、1 isStringは、1 class = Stringでもないし、1 isKindOf: Stringでもないことに注目してください。 あくまで受け取った側が自己申告で答えています。 「ねえねえ、君、X部長のところの人かな?」と訊かれて、組織上は直接の部下でなくても、プロジェクトで一緒だったり、X部長を師と仰いでいるのなら、自分で判断して会話を続ければよいのです。

Smalltalkには、他にも色々な「isナントカ」とか「ifナントカ:」とかがあります。 どれも、受け取ったオブジェクトが自分なりに答えます。 だから答えなきゃならないメッセージの種類は、静的型付けの場合よりも、多くなります。 でも、「俺もこの仕事に関わりたいな」と思った人は、誰に許可を得ることもなく、自分から「はい」と言えます。 そのかわり、「はい」と答えたからには、その仕事を遂行するだけの能力を身につけなければなりません。 さあ、がんばりましょう。ツ

これが、動的型付けでは問題領域を共有する、と言った意味です。

どうでしょう、納得してもらえたでしょうか? ある意味、大企業とベンチャーのようでもあり、伽藍とバザールのようでもあります。 オレ個人は、プログラムが解くべき問題がしっかり定義されていれば、静的型付けの安全性と効率を取るでしょう。 これから問題を創出するような探検的なプロジェクトでは、動的型付けを選びます。

どちらかがもう一方より絶対的に優れているわけではありませんし、 「動的型付け = 静的型付け - コンパイル時型検査」でもありません。 違う特性を持った、違うスタイルと、違う戦略、違う組織論に立った、違う獣なのです。

もちろん、静的型付けが好き!とか、動的型付けはこうしたらもっと良くなる!とか、そういう楽しい議論になれば、楽しいですね。ツ

2011/09/17

オブジェクト指向って何?

オブジェクト指向という言葉が陳腐化して、もうずいぶん時間が経ちましたね。 もうオブジェクト指向は終わった、とか、色々な事が言われています。 オブジェクト指向は本当に終わったんでしょうか?

残念でした。オレにとっては、オブジェクト指向ってのは、これから愉快痛快な展開で面白くなってくるコンセプト。ツ 皆が「終わった」と言っているのは、オブジェクト指向を実現するために使えそうな数多くの技術のうち、 とりあえずということで仮採用したプログラム構成技術の1つにすぎないと思うのです。

さて、では本題。オブジェクト指向って何?

オブジェクト指向とは、計算メディアを使っている人が認識したモノに対してプログラミングをすること、だと思っています。 例えば、このブログに表示されている著者近影のイラスト。眼鏡をかけているのに気付いたでしょうか? 今、あなたは、イラスト中の眼鏡を認識しました。この眼鏡に対して、「レンズの色はヴィヴィッドピンクがいいな。」というメッセージを送って、眼鏡がそのメッセージを受け取って、レンズの色をヴィヴィッドピンクにするのがオブジェクト指向です。

「おいおい、それはオブジェクト指向じゃなくてフォトショだろ!」と思う人が多いことは重々承知しています。ツ その上で、あえて、「そうだね、フォトショはオブジェクト指向から多大な影響を受けたし、グラフィカルなUIでオブジェクト指向を実現するには、画像処理とか画像認識は非常に強力な武器だと思うよ。」と答えます。

つまり、オブジェクト指向というのは、

  1. 人間が何かを認識する。
  2. その「何か」を掴む。
  3. 掴んだ「何か」にメッセージを送る。
  4. 「何か」がメッセージを受け取る。
  5. メッセージに応じて、「何か」が何かをする。
という一連のプロセスから成っています。

一方、巷で「終わった」と言われているオブジェクト指向とは何でしょうか? 情報工学系の技術書ではよく、オブジェクト指向=継承+カプセル化+多態、とか言われたりしますね。 で、クラスベースオブジェクト指向ではクラス階層を使って、プロトタイプベースオブジェクト指向では委譲連鎖を使います。 でも、これって、上に挙げたオブジェクト指向のプロセスの後半だけの話です。

オブジェクト指向の技術書ではまず必ず、メソッドについて言及がありますが、これすらオブジェクト指向そのものにとっては本質ではありません。 受け取ったメッセージに対応するメソッドを探索することは、とりあえず採用している仮の仕組みに過ぎません。 現にSmalltalkやRubyでは対応するメソッドが無くても、対応する処理を記述することができます。

これからオブジェクト指向を本格的に実現していくためには、むしろ前半の仕組みが必要です。 ボタンを押したらダイアログが開く、とか、変数で掴まえた「オブジェクト」にメッセージを投げる、で満足するのは20世紀で卒業しなくっちゃ。ツ 21世紀のオブジェクト指向は、もっと人間の認知に迫ることが必要なんです。 人間が注目している対象を「掴む」ための、入力デバイスや画像認識や自然言語処理や音響認識が必要です。 そして、「掴んだ」対象にメッセージを送るための、インターフェイス上の「言語デザイン」が必要です。 そろそろAltoを越えましょうよ。

オブジェクト指向の研究開発の軸足は、ソフトウェア工学から認知科学、言語学、機械学習、インタラクションデザインに移っていきます。 どうです?オレはワクワクドキドキが止まりませんよ? ツ

2011/09/16

今さらながらブログはじめました。

こんにちは、@tomoodaです。

今まで書きたいことは基本的にtwitterに書いていましたが、やっぱり文字数制限がつらい。ツ コード例とか書くだけでももう大変。それがブログなら

(PGeneGenerator
    on: [ :g | 
        | i j |
        i := 0.
        g yield: i.
        j := 1.
        g yield: j.
        [ 
        i + j
            =>> [ :k | 
                g yield: k.
                i := j.
                j := k ] ] repeat ])
    =>> [ :fibonacci | 
        10
            timesRepeat: [ 
                Transcript
                    cr;
                    show: fibonacci next printString ] ]

がはははは、余裕で書き下せるぜい!勝ったも同然。なお、上記のコードはPharoというSmalltalk処理系でフィボナッチ数列を10個、トランスクリプトウィンドウに表示します。

Smalltalkerにも見慣れないモノがあると思いますが、これは開発中のPGeneというライブラリが実装するジェネレータPGeneGeneratorの例題です。Pythonのジェネレータがあまりにも便利なので、ついSmalltalk上で実装しちゃいました。

Smalltalkには昔からStreamクラスがあり、nextメッセージを投げる毎に次の値を返してくるのですが、いかんせん数列を定義する度にクラスを定義したのでは、「だからクラスベースは…」などと言われてしまいます。

また、Smalltalkには昔からdo:メッセージもあり、クロージャを渡して繰り返し評価をします。しかし悲しいかな、do:はファーストクラスオブジェクトではないのです。複数のオブジェクトから次から次へとnextメッセージを投げるような自由度はありません。

そこでジェネレータです。nextメッセージを受け取ったら、クロージャ[:g | ... ]を評価します。そして、クロージャを評価していく中で、使いたい値が手に入ったらいつでもyield:メッセージを投げれば、その引数がnextメッセージの返り値となります。また、次にnextメッセージを受け取ったら、前回yield:した箇所から実行を再開して、次のyield:がnextの返り値になります。まあ、なんて便利なんでしょう。ツ このように2つの実行コンテキストを交互に継続させることをコルーチンといいます。

Smalltalkは実行コンテキストまでファーストクラスオブジェクトなので、この程度のことはフフンのフンなのです。ツ

次に見慣れない表記は=>>でしょう。一般には、expr =>> [ :name | ... ]という形で使って、exprの評価結果にnameという名前をつけて、...を評価します。これがあると何が嬉しいかと言うと、自然とローカルなスコープになるだけでなく、破壊代入と名前束縛を明示的に分離できるのが最大の利点です。Smalltalkは関数型言語でも論理型言語でもないので、破壊代入は悪ではありません。しかし、濫用せずに、弊害を最小化しながらそのメリットを享受しようじゃないですか。ツ ちなみに、実装としては=>>の左側のオブジェクトを引数にして右側のクロージャを評価しているだけです。

こんな調子で、Smalltalkのこと、オブジェクト指向のこと、プログラミングのこと、その他いろいろ書いていきます。
よろしく! ツ