Diary?

2009-01-24
Sat

(10:23)

うげー、雨かよ。ったくタイミング悪いなおい。

それはともかく、「選挙中にブログ更新」市長を刑事告発って酷い話だなおい。

と最初記事を読んだときは思ったけど、そのブログの内容がねぇ……。俺は今回の件は公職選挙法違反で刑事告発されるのは絶対におかしいと思うが、もっと別の罪状でなら刑事告発まではいかなくとも市長の座を引きずり下ろされるのは仕方ないだろうと思う。

(22:38)

「技術者/プログラマのためのラムダ計算、論理、圏」セミナー』に行ってきた。とりあえずセミナーでやったことを自分なりに手短にまとめてみる。勘違いしてる部分がないとは言えないので、あんまり鵜呑みにしないように。基本的にこういう理解でいいらしいっす。

まず最初に出た話題はスノーグローブ現象という奴なのだが、このスノーグローブというのは水で満たしたガラス玉にミニチュアの人形や建物をいれた玩具。このスノーグローブの中に自分の周囲と完全に同じ環境を構築可能で、実際に構築したと仮定する。この時点で存在する世界は、俺達の生きている世界(リアルワールド)とスノーグローブの中の世界の二つ。

ところがだ、スノーグローブの中に世界を作るためには、まず俺達の生きている世界を別の視点から観察する必要がある。この別の視点というのは、メタとか神とか超越者とか呼ばれている視点(ちなみに俺はメタという言葉がどういうわけか一番しっくりくる)。それでメタでも神でも何でもいいけど、その視点から俺達の世界を見るために、「外の世界」というものを定義しよう。そして「リアルワールドからスノーグローブの世界を観察する行為」と「外の世界から自分の世界を観察する行為」は条件にもよるだろうが基本的に違いがなくなるというのがスノーボール現象(という風に理解した)。

そしてこのスノーボール現象は実際には珍しいものではなく、例えばメタ巡回型インタープリタは「インタープリタにソースコードを食わせる」のと「メタ巡回型インタープリタで動いているインタープリタにソースコードを食わせる」のは傍から見るとまったく違いがない。とりあえずもう少しダイレクトな例を出そう(あ、俺は Python が好きなので Python で例を書くけど、ぶっちゃけ別の言語でもまったく困らない内容なので適宜読み替えて)。

まずは以下のコードを考えてみよう。

$ python
>>> 1 + 2
3

まあ当たり前と言えばそうだが、それじゃあ次の奴はどうだろう。

>>> eval("1 + 2")
3

eval の動作はわかるよな? じゃあ次だ。

>>> eval("eval('1 + 2')")
3

当たり前といえばそうだが、 eval に eval を表現する文字列を渡して実行させれば、そりゃこういう結果になる。そしてここでの eval はスノーボール世界の住人に他ならない。というのも最初の例は「俺が Python インタープリタに 1 + 2 を計算させる」ものだったが、次の例では eval でワンクッション置かれており、 Python インタープリタを直に使う世界とワンクッション置かれて eval が動作する世界は異なった世界なのだが、傍から見た動作、というか結果に関してはまったく区別が付かない。

つまりコンピュータを用いた世界の記述(モデル化)は何がしかのスノーグローブ的性質を持っており、そしてスノーグローブ現象の一例としてのラムダ計算というのが今日のセミナーの主眼だったわけだ。

それでラムダ計算の話に移るが、セミナーの趣旨から言えばちゃんと図を書いて説明するべきなのだろうけど、それやってると全然手短にまとまらないし、後日セミナーの資料はちゃんと公開されるはずだし、とりあえずコードを主体に(かなり乱暴に)まとめてみる。

まずは以下の四つの関数(と一個のネストした関数)を定義する。元々のセミナーでは様々な立場の視点を出すためにもっと多くの関数があり、そもそも関数という言い方ではなかったが、ここではそこらは全部省いてる(数学的視点、論理学的視点、計算機科学的視点の交差点になる部分なので、実は重要なところだと思うんだけど)。一個未定義な関数があるが、まあそのままの意味で受け取って。あと Python の組み込み関数と名前がもろ被りだけど、それはああ勘弁して。

def add(a, b):
    return a + b

def bind(a):
    def binded(b):
        return add(a, b)
    return binded

def compile(a):
    return ... #返すものの詳細は後述

def exec(x, b):
    f = ... #ここで何をしているのかは後述
    return f(b)

関数 add は二つの引数を足した値を返す、見たまんまの関数だ。それに対して関数 bind は若干ややこしい。こいつは一つの引数を受け取り、一つの引数を受け取る関数を返す。この時返される関数は、 bind の引数と自身への引数を足した値を返す。そして compile と eval は二つで一つの関数で、まず compile は一つの引数を受け取り、良く分からないバイナリデータを返す関数だ。具体的に何が返されるのかはご想像にお任せする。そして exec はその良く分からないバイナリデータを第一引数で受け取り解釈して、自身への第二引数と compile への引数を足したものを返す。

この条件下では、以下の三つはすべて等しい。

  • add(a, b)
  • bind(a)(b)
  • exec(compile(a), b)

ところで俺は「compile は良く分からないバイナリデータを返す」と書いたが、ここでその戻り値をファイルに保存可能で、第一引数にそのファイル名を、第二引数に何かしらの数値を受け取る interprete コマンドが使えると仮定しよう。使い方はこんな感じ。

$ interprete filename argument

高階関数とソースコードのコンパイルには実際のところ違いがないと思えてこないか?

その後はセミナーでは以下のような記号体系で話が進んだので、それに倣うことにしよう。

  • f: 二引数の任意の関数
  • ^: "f^E" と記述することで、「fを元にしたEという関数に渡せる一引数の高階関数」
  • E: ^で作られた高階関数を第一引数に、その高階関数への引数を第二引数にとる関数

この場合は任意の引数に対して f(x, y) = E(f^E(x), y) が成り立つ。最初に見たときはちと何じゃこりゃって感じだったが、これは以下のように解釈できる。

  • f: プログラム本体
  • f^E: バイトコード
  • E: バイトコードのインタープリタ
  • x: コンパイル時に定まる変数=定数
  • y: 実行時に渡される変数

いよいよ高階関数を作ることとコンパイルが同じに見えてきた。ここで話をスノーグローブに戻すと、

  • f(x, y): リアルワールドでの事象
  • E(f^E(x), y): スノーグローブの中の事象

という解釈が出来そうだ。 f(x, y) はとあるインタープリタ直下で起こっていることだけど、 E(f^E(x), y) は E と ^ でワンクッション置かれているからな。高階関数を使う(実行可能な関数を返す機構を使う)ということはスノーグローブの世界を作るということになり、それってつまり外の世界の視点を持つことにならないかな?

セミナーでは「メタな視点や抽象思考などのためのイメージトレーニング」もやったり「インフォーマルなラムダ計算」の紹介があったりしたけど、前者は文字に起こすのが困難だし、後者は直観的で便利そうだけど今日のところは割愛しておく。

とにかく今回のセミナーでもっとも俺が衝撃を受けたのはスノーグローブ現象で、俺のプログラミング言語の嗜好及びプログラミング時のメンタルモデルはかなりこいつで説明できそうに思える。俺は気軽に外の世界へ出ていきたいんだ、きっと。

……なんか全然手短にまとまってない気がするけどまあいいや。

Creative Commons
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。