math::category
DESCRIPTION
Perlを使って圏論概念をシミュレーション。TRANSCRIPT
2009-11-21
Math::Categoryid:hiratara
Math::Category とは?(1)
✤ Perlによる圏の計算のシミュレーション
✤ 射、関手、自然変換を実装
✤ 米田埋込関手の実装
✤ モナド、クライスリ射の実装
Math::Category とは?(2)
✤ 射は関数を仮定していない
✤ dom, cod, comp を適切に実装
✤ 関手、自然変換は関数と仮定
✤ 小さなHom集合しか扱えない。実用上は十分
✤ 実用、速度は無視。圏論の言葉をなるべく忠実に翻訳
圏の実装(1)
✤ 圏は射のみで表現
✤ 対象は、恒等射として射で現せる
✤ Morphism は、以下のメソッドを持つinterface
✤ source: domの対象を表す恒等Morphismを返す
✤ target: codの対象を表す恒等Morphismを返す
✤ composition: 射の合成
圏の実装(2)
✤ 圏の性質は、ユーザの責任となる
✤ targetやsourceが本当に恒等射か
✤ composition の結合則
✤ 圏の全体の管理もユーザの責任
✤ 圏の全ての射を得ることはできない
✤ インスタンス化した射が圏のどの部分かは気にしない
圏の例(1): SimpleMorphism
✤ 2つの対象間に最大で1個しか射がない圏の表現。主にテスト用。
✤ simple_morph ‘1’ => ‘2’; のように射を記述
✤ source は simple_morph ‘1’ => ‘1’
✤ target は simple_morph ‘2’ => ‘2’
✤ (simple_morph ‘2’ => ‘3’) . (simple_morph ‘1’ => ‘2’)= (simple_morph ‘1’ => ‘3’)
‘3’
‘2’
‘1’
圏の例(2): SubroutineMorphism(1)
✤ Perlの関数の圏
✤ Perlは型がないので、引数となれる任意の値の集合を対象と考える
✤ 型のある言語であれば、型を対象とすべき
✤ sub_morph { $_[0] * 2 } と関数的に定義
✤ sourceとtargetは同じで、 sub_morph { @_ } (恒等関数)
✤ 対象となる集合にあわせて、臨機応変に制限して考える(ズル)
文字列
undef
圏の例(2): SubroutineMorphism(2)
✤ 合成 $sub2 . $sub1 は、「$sub1 、 $sub2」 の順に実行とする
✤ 実装でも、合成せずにサブルーチンをリストとして持っている
✤ スタックオーバフローとならないようにするため
✤ 副作用は・・・あってもいいんじゃないか?
副作用はあっていいの?(1)
✤ f . ( g . h ) も (f . g) . h も f をやって g をやって h をやる
✤ 同じ意味。結合則が成り立っている
✤ idは副作用があってはいけない(当たり前)
✤ ただし、いい性質の圏ではないかも
✤ f×g: x → ( f(x), g(x) ) は副作用があると積にならない
怪しい議論!
A × BA B
C
f
πA πB
g( f, g )
副作用はあっていいの?(2)
(f,g)にはfとgの副作用が入り可換にならない。
A×Bと射影πAとπBをとる( f, g ): f, gの順に実行して2値にする
結果が外部環境に依存する場合、(f,g)と(g,f)の結果が異なる。
怪しい議論!
圏の例(3): 圏の積、双対圏
✤ 圏の積: bi_morph $morph1, $morph2;
✤ sourceやtarget、合成はそれぞれの射のもの
✤ 双対圏: op $morph;
✤ sourceとtargeが逆。合成も逆。
✤ op op $morph; は $morph になる
(A1, A2)(f, g)
(B1, B2)B
f
A
関手の実装(1)
✤ Functor クラス
✤ 実体は、単なるサブルーチンのラッパー
✤ Morphismを引数とし、Morphismを返す関数
✤ 射関数だけで表現。対象関数はない(対象は恒等射で表しているため、射関数に含まれる)
関手の実装(2)
✤ 関手は、 functor { ... }; と普通の無名関数風に定義する
✤ どの圏からどの圏の関手か、は定義する人が意識する(Morphismの型で判断してもOK)
✤ 恒等射、結合則の保存は、定義する人の責任
✤ ピリオドによって合成できる ( $functor2 . $functor1 )
関手の例(1): $BI_FUNCTOR
✤ Hom(-, -) の実装
✤ C^op × C から Sets への関手
✤ C^op × C は、双対射と圏の積を利用して表現
✤ Sets は SubroutineMorphism で表現(副作用のないものを集めた部分圏をSetsと見なせる)
自然変換の実装(1)
✤ NaturalTransformation クラス
✤ 実体は単なるサブルーチンのラッパー
✤ 恒等射を引数にとり、対応する成分(Morphism)を返す
自然変換の実装(2)
✤ 自然変換は nat { }; と定義する
✤ どの関手間の対応かは、定義する人が意識する
✤ 自然性を満たすかは、定義した人の責任
✤ 引数(恒等射)のチェックは、定義する人が実装するかどうかによる
✤ 一般的には難しい
自然変換の実装(3)
✤ 結合の定義
✤ $nat2 . $nat1 自然変換同士の垂直結合
✤ $funct . $nat 関手と自然変換の水平結合
✤ $nat . $funct 自然変換と関手の水平結合
✤ 関手圏の実装
✤ functor_morph nat { my $id = shift; ... 略 ... return $sum_morph};
✤ sourceやtargetは、各コンポーネントのsourceやtargetを集めたもの
圏の例(4): FunctorMorphism
G
F
τ
関手の例(2): $YONEDA_EMBEDDING
✤ 米田埋込の定義
✤ C^op から 関手圏 Sets^C への関手
✤ 自然変換 Hom(g, -) を返せばいいらしいので、盲目的に定義
米田埋込の利用: CPS変換(1)
✤ 例えば、 uc を CPS変換
my $shout = uc "Sheaf, Category, and Topos";print qq("$shout!"\n);
cps_uc sub { print qq("$_[0]!"\n); }, "Sheaf, Category, and Topos";
米田埋込の利用: CPS変換(2)
my $fun_morph = $YONEDA_EMBEDDING->( op sub_morph { uc shift } );my $cps_uc = $fun_morph->( sub_morph { @_ } );
$cps_uc->( sub_morph { print qq("$_[0]!"\n); })->( "Sheaf, Category, and Topos" );
文字列
文字列
Hom(文字列, -)
Hom(文字列, -)
uc $fun_morph
Hom(文字列, undef)
Hom(文字列, undef)
$cps_uc
$cps_uc->(print)
∈
∈
元の双対圏 関手圏 Setsの圏
Monadの実装(1)
✤ 自己関手functorと自然変換etaとmuをフィールドに持つオブジェクト
✤ eta: I → T, mu: TT → T の自然変換
✤ モナド則は開発者が遵守する
✤ Haskellと違い、クライスリトリプルではなくほんとのMonad
✤ モナド則も違うよ
Monadの実装(2)
✤ モナド則1. 単位元$monad->mu . (funct_nat $monad->functor, $monad->eta)$monad->mu . (nat_funct $monad->eta, $monad->functor)のどちらもT → Tの恒等変換
✤ モナド則2. 結合則$monad->mu . (funct_nat $monad->functor, $monad->mu)$monad->mu . (nat_funct $monad->mu, $monad->functor)が等しい(どちらも TTT → T の自然変換)
Monadの例(1): $LIST_MONAD
✤ 多値の関数を許したのでちょっと面倒くさい
✤ 関手の対象関数は、引数集合を、[v1_1, v1_2, v1_3], [v2_1, v2_2], [v3_1, v3_2, v3_3, v3_4]と、引数となりうる多値を複数持つような集合に移すもの。
✤ 射関数はmap、eta は [] で包むだけ、 mu は concat
Monadの例(2): $STATE_MONAD
✤ これも多値を許したので面倒
✤ 対象関数は、引数の集合を、sub { my @states = @_; .. 略 .. return ¥@values, ¥@new_states }という形式の1値の値が集まる集合に移す
✤ functor, eta, mu は Haskell のそれと同じ定義 (ソース見るべし)
Monadの例(3): Maybeモナド
✤ Maybeモナド。元の値にnothing 値を加えたもの
✤ リファレンス(ポインタ)をとって、null 値を追加することで実装
✤ Listモナドとほとんど同じ実装だなあ・・・
圏の例(5): KleisliMorphism
✤ モナド m、射f: a -> m b、恒等射b にて表現
a b c
m b
m m b m m cm m a
m a m cf g
m gμ c
η cη bη a
圏の例(5): KleisliMorphism
✤ モナド m、射f: a -> m b、恒等射b にて表現
a b c
m b
m m b m m cm m a
m a m cf g
m gμ c
η cη bη a
Kleisli圏の利用例: Maybeモナド(1)
✤ 以下の関数を組み合わせてHTMLから分数を得たいget_number: HTMLから該当箇所を得る(<span>3/10</span>)cut_tag: タグを落とす (3/10)parse_number: 数字を得る (3, 10)div: 数値に直す (0.3)
✤ 右のコードではNG
*parse_rate = ¥&{ (wrap \&div) . (wrap \&parse_number) . (wrap \&cut_tag) . (wrap \&get_number)};
Kleisli圏の利用例: Maybeモナド(2)
✤ エラー処理
Kleisli圏の利用例: Maybeモナド(2)
✤ エラー処理
sub parse_rate { my $html = shift; my $number_str = get_number($html); if( $number_str ){ my $no_tag = cut_tag( $number_str ); if( $no_tag ){ my ($n1, $n2) = parse_number( $no_tag ); if( $n1 ){ my $ret = div( $n1, $n2 ); if( $ret ){ return $ret; } } } }
return undef;}
Kleisli圏の利用例: Maybeモナド(2)
✤ MaybeモナドによるKleisli射だと、そのまま結合できる
Kleisli圏の利用例: Maybeモナド(2)
✤ MaybeモナドによるKleisli射だと、そのまま結合できる
sub parse_rate { my $kleisli_morphism = $div . $parse_number . $cut_tag . $get_number; return eval_maybe $kleisli_morphism, @_;};
まとめと課題
✤ 当たり前だけど、圏論の概念を実装すれば、理論通りに動く
✤ 課題
✤ 数学概念を持ち込むためにラップしまくってるので遅い
✤ いまいちいい用途が見つからない(笑)
✤ 今は実装を膨らませて、可能性を見ていきたい