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

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

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

参照用 記事

CatyScript2.0のpar構文とデカルト積

CatyScript2.0では、「par構文」と呼ぶ構文構成素(syntactic construct)を導入するつもりです。parはparallelの略記で平行実行*1を意味します(実装上は逐次実行でも差し支えありません)。現状でも、複数のプログラム実行単位を平行に実行する構文はあるのですが、実行単位の配置がフォーク形なんです。これを図形的にもパラレルに配置するのがpar構文です。

まずは実例

Catyのコマンドは、デカルト*2の射としてモデル化されるので、圏論をご存知の方はそのように解釈してください。Unix流のパイプ&フィルターをご存知の方は、標準入出力を使うフィルターコマンドだと理解すればOKです。

passは何もしないコマンド、incは入力に1を足して出力するコマンド*3、sqは入力を二乗するコマンドとします。圏の恒等射に相当する総称コマンドpassは組み込みですが、incとsqは次のように定義できます。

Pythonによる実装:


# -*- coding: utf-8 -*-
# arith.py
#
from caty.command import Command

class Inc(Command):
def execute(self, input):
return input + 1

class Sq(Command):
def execute(self, input):
return input * input

コマンドの宣言:


module public; // -*- coding: utf-8 -*-

/** 入力に1を足す */
command inc :: number -> number
refers python:arith.Inc;

/** 入力を二乗する */
command sq :: number -> number
refers python:arith.Sq;

実行例は次のような感じ。


caty:sample> 3 | pass
3
caty:sample> 3 | inc
4
caty:sample> 3 | sq
9
caty:sample>

コマンドを配列に編成すると、フォーク配置で実行されます。


caty:sample> 3 | [pass, inc, sq]
[3, 4, 9]
caty:sample>

もし、[pass, inc, sq] がパラレル配置ならば、入力 [1, 2, 3] に対して [1, 3, 9] が出力されるはずです。そうはなってないので、やってもエラーします。


caty:sample> [1, 2, 3] | [pass, inc, sq]

(... スタックトレース 省略 ...)

値はnumberでなければなりません
caty:sample>

パラレル配置と同値な式を書けるには書けますが、面倒です。


caty:sample> [1, 2, 3] | [(nth 1 | pass), (nth 2 | inc), (nth 3 | sq)]
[1, 3, 9]
caty:sample>

[pass, inc, sq] がフォーク配置であることを変えるわけにはいかないので、パラレル配置であることを par [pass, inc, sq] で示します。つまり、CatyScript2.0では次のようになる予定です。


caty:sample> [1, 2, 3] | par [pass, inc, sq]
[1, 3, 9]
caty:sample>

オブジェクトに対してもpar構文が使えます。


caty:sample> {"a":1, "b":2, "c":3} | par {"a":pass, "b":inc, "c":sq}
{
"a":1,
"b":3,
"c":9
}
caty:sample>

オブジェクトに対するpar構文は、破壊的代入による部分更新のように使えます。次は、プロパティbだけを5に書き換えます。


caty:sample> {"a":1, "b":2, "c":3} | par {"a":pass, "b":5, "c":pass}
{
"a":1,
"b":5,
"c":3
}
caty:sample>

もっと簡単に書きたいなら:


caty:sample> {"a":1, "b":2, "c":3} | par {"b":5, *:pass}
{
"a":1,
"b":5,
"c":3
}
caty:sample>

デカルト圏における解釈

par構文は、圏論的に言えば射のデカルトに過ぎません。つまり、par [f, g] は f×g のことです。集合圏における f×g の定義は次のとおり。

  • (f×g)(x, y) = (f(x), g(y))

CatyScriptの構文では次のようになります。

  • [x, y] | par [f, g] ⇔ [x | f, y | g]

CatyScriptの配列式 [f, g] は、射fと射gのデカルトペアリング <f, g> *4なのです。

  • <f, g>(x) = (f(x), g(x))
  • x | [f, g] ⇔ [x | f, x | g]

射のデカルト積とデカルトペアリングは、次のようにして相互に定義可能です。Δは対角射で、π1とπ2は射影です。

  • <f, g> := Δ;(f×g)
  • f×g := <π1;f, π2;g>

この事実をCatyScriptの構文で書くならば次のようになります。

  • [f, g] ⇔ [pass, pass] | par [f, g]
  • par [f, g] ⇔ [(nth 1 | f), (nth 2 | g)]

先ほど「パラレル配置と同値な式を書けるには書けます」と言ったのは、パラレル配置(=デカルト積)を [(nth 1 | f), (nth 2 | g)] で代用できるということです。でも、これは面倒すぎますから、組み込み構文にしたいわけね。

オブジェクト式の場合も、理論的には配列式と変わりません。オブジェクトは名前付きタプルなので、これもデカルト積の表現となっています。

par構文をMaybeモナドやListモナドと一緒に使うとどうなるか? という問題があります。これに関しては、CatyScript2.0における機能的仕様は完全に出来てますが、構文で悩んでいます。ものすごく悩んでいます。構文の方針が決まったら、ベースのデカルト圏をモナドでクライスリ拡張する方法も紹介します。

*1:図形的なイメージを強調して「平行」を使っています。「並列」と「並行」(平行じゃない)の違いは、ku-ma-meさんの「parallel と concurrent 、並列と並行の覚え方」にまとまってます。

*2:正確にはデカルト半環圏というべきです。

*3:[追記]incrementよりsuccessorでsucが良かったな、とか思ってますが、まーいいや。[/追記]

*4:2つならペアリングですが、一般的には射のデカルトタプリングです。