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は静的型付けでも相性いいですよ。 HaskellMaybeモナドの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を越えた向こうのほうにある未来を覗き見るのが大好きなのです。ツ