Posted on December 15, 2019

概要

広い範囲の多項式Functorについて、Monadになれるかどうかがわかった。

準備

ここでは、多項式Functorを、次の形をしたFunctor F のことと定義します。

F(x) ~ x^(a_0) + x^(a_1) + x^(a_2) + ... + x^(a_k)

ただし、 ~ は x について自然な同型、 + は直和型、 x^a は x の a 個の直積とします。 また、項の数 k は0以上、すなわち F(x) は Void でもよいとします。また、 a_i も0でもよいとします。 x^0 はUnit型、ただ1つの値のみを持つ型です。

Remark. 有限個の値をとる型 e に対して、 F(x) = Either e x, G(x) = (e,x), H(x) = e -> x はすべて多項式Functorです。 同じく Const e も多項式Functorです。 多項式Functor F, G について、 (F :+: G)(x) = F x + G x , (F :*: G)(x) = F x * G x は多項式Functorです。 多項式Functorの合成は多項式Functorです。 多項式Functor F は常に Traversable にできます。



よく知っているMonadのほとんどが多項式Functorです。ただし、ここでは e を有限な型とします。

Identity Functor newtype Id x = Id x

Either e

(,) e = Writer e

(->) e = Reader e

Maybe = Either ()

Proxy = Const ()

多項式Functorでないものももちろんあります。例えば Cont r がそうです。

また、すでにあるMonadのインスタンスから、新たなインスタンスを次々構成する方法が多数あります。 各種Monad Transformerなどはおなじみかと思いますが、それ以外にも次のようなものがあります：

本論

ある多項式Functor F に対して、 F のMonadのインスタンスが定義できるだろうか？という問題を考えます。 まず、これが常に可能であるわけではありません。例えば、 F ~ Const e は e が2つ以上の異なる値を持つときにMonadになりません。加えて、先日の記事では以下の事実を示しました。

F ~ Maybe (e -> x) は、 e が2つ以上の異なる値を持つときにMonadにならない

一方で、非常に広い範囲の多項式Functorに対してMonadのインスタンスが定義できることもわかっています。 これらを組み合わせて、任意の多項式Functorに対してMonadのインスタンスが定義できるかどうかを判定する方法を示します。

主張0: ある多項式Functor F が F(x) ~ c と書けるならば、 |c| = 1 のとき、またそのときに限り F のMonadのインスタンスが存在する。

が と書けるならば、 のとき、またそのときに限り のMonadのインスタンスが存在する。 主張1: ある多項式Functor F が F(x) ~ x * G(x) と書けるならば、 F のMonadのインスタンスが存在する。

が と書けるならば、 のMonadのインスタンスが存在する。 主張2: ある多項式Functor F が F(x) ~ 1 + x + G(x) と書けるならば、 F のMonadのインスタンスが存在する。

が と書けるならば、 のMonadのインスタンスが存在する。 主張3: ある多項式Functor F が F(x) ~ c + x^2 * G(x) ( |c| >= 1, G(x) ~/~ 0 )と書けるならば、 F のMonadのインスタンスが存在しない。

が ( )と書けるならば、 のMonadのインスタンスが存在しない。 結論: F(x) ~ c + x * b + x^2 * A(x) に対してMonadのインスタンスが存在する ⇔ 以下のいずれかが成り立つ c = 1, b = 0, A(x) ~ 0 c = 0, b = 0, A(x) ~/~ 0 b >= 1



主張0 ~ 主張3から結論が出ることは簡単に確かめられます。

主張0

ある多項式Functor F が F(x) ~ c と書けるならば、 |c| = 1 のとき、またそのときに限り F のMonadのインスタンスが存在する。

証明

（以下、証明部分は常体で書きます）

(⇒)はProxyと同じようにMonadのインスタンスが定義できることからわかる。

(⇐)を示す。 pure :: x -> F x はある定数関数 const c0 である。このとき、 c0 :: F x ~ c である。 任意の c1 :: F x ~ c について、 join (pure c1) = join (const c0 c1) = join c0 となるが、Monad則より join . pure = id なので join (pure c1) = c1 でもある。したがって、任意の c 型の値は join c0 に等しい。これはすなわち、 |c| = 1 を意味する。

主張1

ある多項式Functor F が F(x) ~ x * G(x) と書けるならば、 F のMonadのインスタンスが存在する。

例をあげると、

F(x) ~ x + x^3

F(x) ~ x^2 + x^5 + x^6

のように、「定数項」のないFunctorはMonadにできます。

証明

F(x) の項の数 k および多項式の次数に関する帰納法を用いる。 k=1 のとき、すなわち F(x) = x^a のとき、 F のMonadのインスタンスがあるのは明らか。

k>1 , かつ G(x) ~ x * H(x) と書けるならば、 G(x) の次数は F(x) より小さいので、帰納法の仮定を用いれば G(x) はMonadにできる。 F(x) ~ x * G(x) ~ (Id * G)(x) かつIdもGもMonadなので、Monadの積によって F(x) もMonadのインスタンスを持てる。

, かつ と書けるならば、 の次数は より小さいので、帰納法の仮定を用いれば はMonadにできる。 かつIdもGもMonadなので、Monadの積によって もMonadのインスタンスを持てる。 k>1 , かつ G(x) ~ x * H(x) と書けないならば、 G(x) ~ 1 + H(x) と書ける。このとき、 F(x) ~ x * (1 + H(x)) ~ x + x * H(x) ~ x + (Id * H)(x) かつ、 (Id * H)(x) の項の数は F(x) より一つ少ないので、帰納法の仮定を用いれば (Id * H)(x) はMonadにできる。 よって、先程述べた“単位の付加”の構成により、 F(x) ~ x + (Id * H)(x) はMonadにできる。

主張2

ある多項式Functor F が F(x) ~ 1 + x + G(x) と書けるならば、 F のMonadのインスタンスが存在する。

例:

F(x) ~ c + b * x , 言い換えれば F x = Either C (B, x) であるとき、 c >= 1 , b >= 1 ならばMonadのインスタンスが存在します。 F x ~ WriterT B (Either C) x 、および任意の空でない型 B に対して Monoid B が定義できることを考えれば、これは納得がいきます。

, 言い換えれば であるとき、 , ならばMonadのインスタンスが存在します。 、および任意の空でない型 に対して が定義できることを考えれば、これは納得がいきます。 F(x) ~ 1 + x + x^2 + x^3 , 長さが 0 〜 3 のリストにはMonadのインスタンスが存在します。

証明

どの多項式FunctorもTraversableにできるのであった。 G(x) も Traversable であると仮定してよい。 次のように F x ~ U g x = 1 + x + g x とそのMonadのインスタンスを定義する。

これがMonad則を満たすことを確認する。

主張3

2019.12.16 追記 ― 証明を読みやすく、あと有限性に依存しないようにできたので修正版を投稿しました

ある多項式Functor F が F(x) ~ c + x^2 * G(x) ( |c| >= 1, G(x) ~/~ 0 )と書けるならば、 F のMonadのインスタンスは存在しない。

例:

F(x) ~ 1 + x^2

F(x) ~ 3 + x^2 + x^4

証明

まず、どんな多項式Functorに対しても、 forall x. x -> F x という型を持つ関数があれば、それは F(x) の特定の項 F(x) ~ ... + x^n + ... に対応するコンストラクタに、 n 個の x のコピーを渡した値を返す。このコンストラクタは、当然、 x に依存せずに決まる。

いま問題としている F(x) ~ c + x^2 * G(x) において、Monadの定義における return :: forall x. x -> F x に対応する項が x^n であるとしたら、 n == 0 であるか、または n >= 2 である。

仮に n == 0 であったとする。これはすなわち return は定数関数であり、 return x の値は x に依存しないことを意味している。しかし、Monad則より join (return fx) = fx でなければならず、また空でない型 X において fx :: F X は2つ以上の異なる値を持つので、 return は定数関数であってはならない。したがって、 n >= 2 でなければならない。

return の返り値に対応する項 x^n を明示して、 F(x) が以下のように定義されているとする。

また、前提条件として与えられている、 C に1つ以上の値があることが、次のように表されているとする。

簡単にわかることとして、 join zero = zero がある。

Monad則から、 join :: F (F x) -> F x の満たすべき等式として次のものが得られる。

以降、この等式は joinUU という名前で呼ぶ。また、これは次のように証明できる。

ここで、次の関数を考える。

いま、ある i :: N において at i x が x に依存しないと仮定する。 このとき、次の sweep f は f i の値に依存しないはずである。

一方、 join $ sweep f を join の結合則を用いて計算すると、以下のようになる。

これは明らかに f の任意の点 i :: N での値に依存する。これは矛盾である。

事実1: 任意の i に対して at i x は x に依存する。

次に、 at i /= return を示す。

いま、 N には2つ以上の異なる値があるので、 i' /= i なる i' :: N が存在して、 U f は f i にも f i' にも依存する。 しかし、 at i (f i) は f i にしか依存しないので、 at i = return ではありえない。

事実2: 任意の i に対して at i /= return

加えて、

より、 hole f i (at k) が f i に依存するかどうかは、 i == k かどうかによって決まる。したがって、 i /= k のとき、 at i /= at k である。

事実3: 任意の i, k :: N に対して、 i /= k ならば at i /= at k

いま、 at i x は x に依存するのであった。 return のときと同様の議論によって、 at i x が返すコンストラクタは2つ以上の x の値に依存することができるので、関数 con :: N -> x -> x -> F x が存在して、 con i x y は x と y の両方に依存し、 con i x x = at i x となるようにできる。

con i に関する等式をいくつか示す。

いま、次の関数 extra を考える。

次の計算によって、2つのことがわかる。

まず、 extra は x に関して自然なので、任意の x, y, f に関して extra i x y f = U _ とならなければならない。 また、 extra i x y f は任意の j /= i における f j と、 x , y のいずれか片方に依存する。

さらに、

である。したがって、 extra i x y f は x と y の両方に依存する。

さて、 extra i x y f = U g と書くことができ、 g :: N -> x なのであった。 g は任意の j /= i における f j 、 x 、そして y のいずれにも依存する。しかし、 N が有限であればこれは N+1 個の独立な変数なので、これは不可能である。

事実4: N は無限個の異なる値をとる。

さらに、 at i は各 i について異なる必要があるので、

事実5: F(x) は無限個の項の和である。