このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

お絵描き圏論とCatyScriptの変数など

久しぶりにCatyScriptとそのモデル(意味論)の話。([追記]実際にソフトウェアを動かしてみたい方はコチラを参照。[/追記]

ここのところずっとCaty上のアプリケーションを作る作業をしてまして、Caty本体の開発がサクサク進んでいるわけじゃないのですが、それでもジワジワとCatyの機能は増えています。

ごく最近、CatyScriptで変数が使えるようになりました。んじゃ今まで変数が使えなかったのか? はい、使えませんでしたよ。CatyScriptのモデルでは、そもそも変数概念があからさまに現れることはなく、変数なんて要らないのです。しかし、現実的な話として「変数なしプログラミング」は辛いです。そこでもう我慢ならんから入れよう、と(まだ実装は完全じゃないですが)。

で、今日の話は、「理屈の上では変数が要らない」、その理由です。これは、CatyScriptのモデル(意味論)を調べれば明らかになることです。CatyScriptのモデルはとある圏なのですが、組み込みの機能を定式化するだけでも、クリーネスター付きのインデックス付きデカルト半環作用圏(indexed cartesian semiringal effect category with Kleene-star)という、長たらしい形容詞が付いた圏となります*1。とりあえずは単なるデカルト圏で話をしますが、それでも事の本質は十分伝わります。

説明の道具にお絵描き圏論=絵算を使います。

内容:

  1. 射のプロファイルと結合
  2. 入力、出力、パラメータ
  3. 変数の生成と初期化
  4. 終端記号と変数の参照
  5. 変数ポイントとワイヤーストッパー
  6. 2つの例題
  7. 変数なんて要らないよ

射のプロファイルと結合

圏の射fと、域X、余域Yの組み合わせを f:X→Y と書きます。X→Y をfのプロファイルと呼びましょう。CatyScriptでは、コロンを2個 '::' 使って、矢印はアスキー文字2文字の組み合わせ '->' で表現します。次のようですね。

  • f :: X -> Y

これに対応する絵は次のとおり。

2つの射 f, g の結合(合成;composition)は、f;g とか g・f とか書きますが、CatyScriptではパイプ記号を使って f | g とします。f :: X -> Y 、g :: Y -> Z のとき、結合 f | g のプロファイルは X -> Z となり、次のように図示できます。

なお、入力がデカルト圏の終対象のときは、左のワイヤーを省略することがあります。普通の言葉でいえば、「入力がvoidなら特に描かない」のです。

入力、出力、パラメータ

f :: X -> Y において、fをコマンド、Xを入力データの型、Yを出力データの型と解釈します。入力と出力以外に、パラメータと呼ぶデータがあります。パラメータのデータ型をPとして、パラメータ付きコマンドのプロファイルは次のように書きます。

  • f P :: X -> Y

対応する絵は:

考えている圏はデカルト圏なので、f P :: X -> Y の代わりに f' :: P×X -> Y を使っても同じことです。パラメータという概念を入れているのは、次の理由からです。

  1. コマンドライン構文で、コマンドのオプションや引数を使うのは自然。
  2. コマンド名オーバーロードを解決するときに、パラメータのシグニチャを使う。

パラメータがいやなら、オーバーロードされたコマンドを複数の異なるコマンドに分けて、パラメータを直積により入力に押しこんでしまえば、入力と出力だけの議論に還元できます。現実的には、パラメータは必須機能だと思いますけどね。

変数の生成と初期化

CatyScriptでは、リダイレクト記号 '>' を使って変数への“代入”を行います。f > x とすると、コマンド(圏の射)の出力を変数xに代入できます。標準出力をファイルにリダイレクトすることに似てますが、次の点が違います。

  1. x が既に存在する変数のとき、f > x は失敗する
  2. f > x をしても出力は失われず、出力をそのまま使うことができる。

一番目の制限は、要するに単一代入だということです。一度バインドした値は変更できません。つまり、変数の生成と初期化は同時に行われ、それ以降イミュータブルになります。

二番目は分かりにくい表現ですが、言ってる内容は単純です。OSのシェル、例えばbashだと、echo hello > foo > bar とか echo hello > foo | grep ello とかが期待通りに動きませんが、CatyScript では動くってことです。もっとも、何を期待するかによりますがね:


caty:root> "hello" > foo; %foo
"hello"
caty:root> "hello" > foo > bar; %bar
"hello"
caty:root> "hello" > foo > bar; [%foo, %bar]
[
"hello",
"hello"
]
caty:root>

OSシェルで言えば、'>' のところで必要に応じてteeコマンドを使っていると思えばいいのです。


$ echo hello > foo; cat foo
hello

$ echo hello | tee foo > bar; cat bar
hello

$ echo hello | tee foo > bar; (cat foo; cat bar)
hello
hello

$

"hello" > foo > bar というリダイレクトの繰り返しは、bar = foo = "hello" という形の代入式とまったく同じです。

終端記号と変数の参照

上のCatyScriptの実行例(Caty対話型シェル)で既に出てきていますが、セミコロン(';')はパイプラインを終了させます。出力はセミコロンのところで捨てられて、それより先(右側)には渡されません。

生成初期化した変数を参照するにはパーセント記号('%')を使います。%foo と書けば、fooという名前の変数値を参照できます。構文的には、定数が書けるところならばどこにでも変数参照が書けます。変数参照が出現したら、その場所に直接データリテラルを書いたのと同じ扱いです。あるいは、%foo を cat foo のように思えばいいでしょう。

変数ポイントとワイヤーストッパー

さて、お絵描き圏論に戻りましょう。f > x という代入式は、次のような絵で表現します。

ワイヤー(線)はデータが流れる道筋の表現です。変数はそのワイヤー上の点(ポイント)に過ぎません。変数を表すワイヤー上の点を変数ポイントと呼びましょう。

f > x は、fから右に出るワイヤー上にxという名前の変数ポイントを作ります。変数ポイントが作られてもデータが失われることはありません。

パイプラインを終端する f ; は、fから右に出るワイヤーに、それ以上は繋げないようにストッパーを付けます。

上の絵で、fからの出力(右側のワイヤーを右に流れるデータ)は捨てられて、それ以上は利用できません。

2つの例題

今までの話で、次の記号に対応する絵の描き方を説明しました。

  1. '|' -- パイプ記号: 射(箱)をワイヤーで結合する
  2. '>' -- リダイレクト記号: ワイヤー上に変数ポイントを作る
  3. ';' -- セミコロン: ワイヤーにストッパーを付ける
  4. '%' -- パーセント記号: 変数参照、変数ポイントからワイヤーを引いて来る

少し複雑な例題をやってみましょう。

  • f > x | g > y ; %x | h %y

絵の描き方のルールを思い出して、ジッと眺めれば分かると思いますよ。

  • %t | f > x; g > y; {"a": %x, "b": %y} | h > z ; k %x %y %z

これはちょっと補足説明が必要ですね。

絵の中央付近にある閉じ中括弧をデカクした記号は、いくつかの入力からJSONオブジェクトデータを作る操作を表します。同様に、閉じ大括弧をデカクした記号は、いくつかの入力からJSON配列データを作る操作です。

コマンドkに3つの引数(位置パラメータ)が付いているのは、パラメータデータとして [%x, %y, %z] という配列が使われることなので、いったん長さ3の配列を作って、それをkの上から繋いでいます。箱の上側に、パラメータデータを入力する端子が付いているとしています。

変数なんて要らないよ

出来上がった絵から、変数ポイントに付いている名前を消し去っても絵のトポロジーは何も変わりません。変数ポイントの名前は、ワイヤリング(配線)の作業途中では目印に使いますが、変数の名前はデータフロー構造に何の影響もないのです。

実際、変数ポイントを使って描ける絵は、変数を使わずに描くことができます。そのとき、対角Δ、終射!、対称σなどの特殊な射を使いますが、これら特殊な射と一般の射の組み合わせで任意のデータフロー構造を組み立てることができます。絵じゃなくてテキスト表現のときでも、記号としてのΔ、!、σの組み合わせを使えば変数は不要です。

つまり、変数なんて要らないんですよ

だけどね、冒頭で言ったように、「変数なしプログラミング」は辛いのよ。辛さに負けて変数入れました、とさ。

*1:[追記]http://twitter.com/#!/hiyama_on_caty/status/31866296186441728 によると、もっと長い形容詞が付いてましたね。inclusive で with generalized Kleisli extensions と。extensionが複数形なのは、実際にいくつかの拡張が付属しているからです。[/追記]