ラベル PGene の投稿を表示しています。 すべての投稿を表示
ラベル 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/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/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のこと、オブジェクト指向のこと、プログラミングのこと、その他いろいろ書いていきます。
よろしく! ツ