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. メッセージに応じて、「何か」が何かをする。
という一連のプロセスから成っています。

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

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

これからオブジェクト指向を本格的に実現していくためには、むしろ前半の仕組みが必要です。 ボタンを押したらダイアログが開く、とか、変数で掴まえた「オブジェクト」にメッセージを投げる、で満足するのは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のこと、オブジェクト指向のこと、プログラミングのこと、その他いろいろ書いていきます。
よろしく! ツ