主張
ある多項式FunctorF
が F(x) ~ c + x^2 * G(x)
(|c| >= 1, G(x) ~/~ 0
)と書けるならば、F
のMonad
のインスタンスは存在しない。
例:以下のFunctorには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)
が以下のように定義されているとする。
type N = ...
type C = ...
type G x = ...
data F x = U (N -> x) | L C | R x x (G x)
return :: forall x. x -> F x
return x = U (const x)
また、前提条件として与えられている、C
に1つ以上の値があることが、次のように表されているとする。
-- |C| >= 1
c0 :: C
zero :: forall x. F x
= L c0 zero
簡単にわかることとして、join zero = zero
がある。
L c0)
join (-- fmap f (L c) = L c
= join (fmap return $ L c0)
= join . fmap return $ L c0
-- join . fmap return = id
= L c0
Monad則から、join :: F (F x) -> F x
の満たすべき等式として次のものが得られる。
-- ∀a :: N -> N -> x
$ U (\i -> U (\j -> a i j)) = U $ \i -> a i i join
以降、この等式はjoinUU
という名前で呼ぶ。また、これは次のように証明できる。
coord :: F (F (N, N))
= U $ \i -> U $ \j -> (i,j)
coord
fmap fst (join coord)
= join (fmap (fmap fst) coord)
= join (U $ \i -> U $ \j -> i)
= join (U $ \i -> return i)
= join (fmap return (U $ \i -> i))
-- Monad則(join . fmap return = id)
= U $ \i -> i
fmap snd (join coord)
= join (fmap (fmap snd) coord)
= join (U $ \i -> U $ \j -> j)
= join (return $ U $ \j -> j)
-- Monad則(join . return = id)
= U $ \j -> j
= U $ \i -> (i,i)
join coord
$ U (\i -> U (\j -> a i j))
join = join $ fmap (fmap (uncurry a)) coord
= fmap (uncurry a) $ join coord
= fmap (uncurry a) $ U $ \i -> (i,i)
= U $ \i -> a i i
ここで、次の関数を考える。
at :: forall x. N -> x -> F x
= join $ U (\j -> if i == j then return x else zero) at i x
以下の事実がわかる。
- 任意の
i
に対してat i x
はx
に依存する。
これを示すため、いま、あるi :: N
においてat i x
がx
に依存しないと仮定する。
このとき、次のsweep f
はf i
の値に依存しないはずである。
sweep :: forall x. (N -> x) -> F (F x)
= U $ \j -> at j (f j) sweep f
一方、join $ sweep f
をjoin
の結合則を用いて計算すると、以下のようになる。
$ sweep f
join = join $ U (\j -> at j (f j))
= join $ U (\j -> join $ U (\k -> if j == k then return (f j) else zero))
= join . fmap join $ U (\j -> U (\k -> if j == k then return (f j) else zero))
-- 結合則
= join . join $ U (\j -> U (\k -> if j == k then return (f j) else zero))
-- joinUU
= join $ U (\j -> if j == j then return (f j) else zero)
= join $ U (\j -> return (f j))
= join . fmap return $ U (\j -> f j)
-- 単位則
= U f
これは明らかにf
の任意の点i :: N
での値に依存する。これは矛盾である。
よって、仮定したことの否定、任意のi :: N
に対してat i x
はx
に依存する
また、at i
に関するさまざまな性質が、Monad
則を仮定すると得られる。
join $ at i (U f) = at i (f i)
… (at-U)
$ at i (U f)
join = join . join $ U $ \j -> if i == j then return (U f) else zero
= join $ U $ \j -> if i == j then (join . return) (U f) else join zero
-- join . return = id = join . fmap return
-- join zero = zero = join (return zero)
= join $ U $ \j -> if i == j then (join . fmap return) (U f) else join (return zero)
= join . fmap join $ U $ \j -> if i == j then (join . fmap return) (U f) else join (return zero)
-- 結合則
= join . join $ U $ \j -> if i == j then fmap return (U f) else return zero
-- fmap return (U f) = U (return . f)
-- return zero = U (const zero)
= join . join $ U $ \j -> if i == j then U (return . f) else U (const zero)
= join . join $ U $ \j -> U (\k -> if i == j then return (f k) else zero
-- joinUU
= join $ U $ \j -> if i == j then return (f j) else zero
-- i == j より j を i に置換
= join $ U $ \j -> if i == j then return (f i) else zero
= at i (f i)
さらに、次の補助関数squash, hole
を定義する。
squash :: N -> (forall x. x -> F x) -> F N
= join $ U $ \j -> if i == j then u j else return j
squash i u
hole :: forall x. N -> F x -> F x
= join $ U $ \j -> if i == j then zero else fx hole i fx
squash i (at i) = U id
.. (squash-at)
squash i (at i)= join $ U $ \j -> if i == j then at i j else return j
= join $ U $ \j ->
if i == j
then join $ U (\k -> if i == k then return j else zero)
else join . return . return $ j
= join . fmap join $ U $ \j ->
if i == j
then U (\k -> if i == k then return j else zero)
else U (\_ -> return j)
-- 結合則 + joinU
= join $ U $ \j ->
if i == j
then if i == j then return j else zero
else return j
= join $ U $ \j -> return j
= join . fmap return $ U (\j -> j)
= U id
hole i (at i x) = zero
… (hole-at)
hole i (at i x)= join $ U $ \j -> if i == j then zero else (at i x)
= join $ U $ \j ->
if i == j
then zero
else join $ U $ \k -> if i == k then return x else zero
-- join . return = id
= join $ U $ \j ->
if i == j
then join $ return zero
else join $ U $ \k -> if i == k then return x else zero
= join . fmap join $ U $ \j -> U $ \k ->
if i == j
then zero
else if i == k then return x else zero
-- 結合則 + joinUU
= join $ U $ \j ->
if i == j
then zero
else if i == j then return x else zero
= join $ U $ \j -> zero
= join $ return zero
= zero
いま、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
となるようにできる。さらに、次の式が成り立つ。
hole i (con i x y) = zero
… (hole-con)
これは、hole
がx
に関して自然、すなわちx
型の変数の内容に依存して処理を変えることがないことから
hole i (con i x y)
と hole i (con i x x) = hole i (at i x)
を区別できないことから、
(hole-at)を組み合わせるとわかる。
at i (con i x y) = con i x y
… (at-con)
$ at i (con i x y)
join = join . join $ U $ \j -> if i == j then return (con i x y) else zero
-- 結合則
= join . fmap join $ U $ \j -> if i == j then return (con i x y) else zero
= join $ U $ \j -> if i == j then join (return (con i x y)) else join zero
-- (hole-con)より、
-- join zero = zero = hole i (con i x y)
= join $ U $ \j ->
if i == j
then join $ return (con i x y)
else hole i (con i x y)
= join $ U $ \j ->
if i == j
then join $ return (con i x y)
else join $ U (\k -> if i == k then zero else con i x y)
= join . fmap join $ U $ \j ->
if i == j
then return (con i x y)
else U (\k -> if i == k then zero else con i x y)
= join . fmap join $ U $ \j -> U $ \k ->
if i == j
then con i x y
else if i == k then zero else con i x y
-- 結合則 + joinUU
= join $ U $ \j ->
if i == j
then con i x y
else (if i == j then zero else con i x y)
= join $ U $ \j -> con i x y
= join . return $ con i x y
= con i x y
いま、次の関数extra
を考える。
extra :: forall x. N -> x -> x -> (N -> x) -> F x
= join $ U $ \j -> if i == j then con i x y else return (f j) extra i x y f
extra
の特別な場合について計算する。
extra i (f i) (f i) f= join $ U $ \j -> if i == j then con i (f i) (f i) else return (f j)
= join $ U $ \j -> if i == j then at i (f i) else return (f j)
= join $ U $ \j -> if i == j then fmap f (at i i) else fmap f (return j)
= join . fmap (fmap f) $ U $ \j -> if i == j then at i i else return j
-- i == j より i と j を置換
-- join . fmap (fmap f) = fmap f . join
= fmap f . join $ U $ \j -> if i == j then at i j else return j
= fmap f $ squash i (at i)
-- (squash-at)
= fmap f $ U id
= U f
extra
はx
に関して自然なので、任意のx, y, f
に関して、
あるg :: N -> x
を用いてextra i x y f = U g
とならなければならない。
このことを用いると、次のような計算ができる。
$ at i (extra i x y f)
join = join $ at i (U g)
-- (at-U)
= at i (g i)
しかし、extra
の定義にもとづいて別の計算を行うと、
$ at i (extra i x y f)
join = join . join $ U $ \j ->
if i == j
then extra i x y f
else zero
-- extraの定義, join . return = id
= join . join $ U $ \j ->
if i == j
then join $ U (\k -> if i == k then con i x y else return (f k))
else join (return zero)
= join . join . fmap join $ U $ \j ->
if i == j
then U (\k -> if i == k then con i x y else return (f k))
else return zero
= join . join . fmap join $ U $ \j -> U $ \k ->
if i == j
then if i == k then con i x y else return (f k)
else zero
-- 結合則 + joinUU
= join . join $ U $ \j ->
if i == j
then if i == j then con i x y else return (f j)
else zero
= join . join $ U $ \j -> if i == j then con i x y else zero
= join $ at i (con i x y)
-- (at-con)
= con i x y
である。しかし、at i (g i)
はx,y
のうち高々一つにしか依存できないのに対し、con i x y
は両方に依存する。
これは矛盾である。したがって、Monad
則を満たすようにF
のMonad
のインスタンスを与えることはできない。
疑問点
ただちょっとわからない点があって、N
が無限であることを許したとき、x == y
ならばf x = f y
を保証できるような(==) :: N -> N -> Bool
が定義できるか?という問題がある。
この証明は、p x = True
をみたすx
がちょうど1つだけ存在するような p :: N -> Bool
が一つでもあれば、(i ==)
の代わりにそれを使うように書き換えるのは難しくないはずなので、このようなp
が存在しないかもしれない、という問題といってもいい。
まず、|N| >= 2
なる条件をHaskellで表現するなら、次の両方が成り立つことだ(と思う)。
- ある
inj :: Bool -> N
が存在して、任意のX :: Type
,f, g :: X -> Bool
に対してinj . f = inj . g
ならばf = g
- ある
surj :: N -> Bool
が存在して、任意のX :: Type
,f, g :: Bool -> X
に対してf . surj = g . surj
ならばf = g
しかし、これを満たすにもかかわらず、(==)
が定義できないような型がある。先程の条件を満たすp
も無い。
newtype BitStream = BitStream (Bool, BitStream Bool)
inj :: Bool -> BitStream
= BitStream (x, inj x)
inj x
surj :: BitStream -> Bool
BitStream (x, _)) = x surj (
ただ、N = BitStream
のとき、join
は全域関数にできないだろう。一般に、N
が(==)
が定義できない(or 条件を満たすp
が無い)ような型ならば、join
は全域関数にできないと言えるか?
2019.12.17 追記 なんか一晩寝たら解決した!
BitStream
には、次の関数search
が定義できる。search
は、
ある全域関数p :: BitStream -> Bool
をとり、
p bs = True
となるbs
があればそのようなbs
を返す。
さもなくば、適当なbs :: BitStream
を返す。
p
が全域関数ならばsearch p
も常にbottom以外の値を返す。
cons :: Bool -> BitStream -> BitStream
= BitStream (b, bs)
cons b bs
search :: (BitStream -> Bool) -> BitStream
= let candidate = search (p . cons False)
search p in if p (cons False candidate)
then cons False candidate
else cons True (search (p . cons True))
forSome :: (BitStream -> Bool) -> Bool
= p (search p) forSome p
F = ReaderT BitStream Maybe
は、当然にMonad
である。一方、
type F x = BitStream -> Maybe x
zero :: forall x. F x
= const Nothing
zero
isZero :: forall x. F x -> Bool
= not . forSome (isJust . mx) isZero
と定義すると、isZero mx = True
⇔ mx = zero
なので、F x
は少なくとも一つの”定数項”と、それ以外の直和である。
isZero mx = False
となるようなmx :: F X
のうち、“自由度”(x^n
の n
)が1のものがあったとする。このとき、以下のようになるはずである。
x :: X
が存在して、x <$ mx = mx
- 任意の
my :: F Y
について、x <$ my = mx
なら、y :: Y
が存在してy <$ my = my
しかし、次のように定義されるmi
を考えよう。
mi :: F BitStream
= join $ \bs -> Just (bs <$ mx) mi
<$ mi
x = x <$ (join $ \bs -> Just (bs <$ mx))
= join . fmap (x <$) $ \bs -> Just (bs <$ mx)
= join $ \bs -> Just (x <$ (bs <$ mx))
= join $ \bs -> Just (x <$ mx)
= join $ \bs -> Just mx
= mx
なので、i :: ByteStream
が存在して、i <$ mi = mi
である。
p :: ByteStream -> Bool
= isJust . mi p
を考えると、
p j = True
となるようなj
が存在する。p j = True
ならば、mi j = Just j
である。p j = True
ならば、mi j = (i <$ mi) j = Just i
である。p j = True
ならばi = j
である。
となるが、このような性質を持つ(全域関数)p
は存在しないのだった。したがって、F X
に”自由度”が1の項は存在しない。
よって、F = ReaderT BitStream Maybe
は
- “定数項”とそれ以外との直和に書かれる
- “自由度”が1の項は存在しない
ので、多項式Functorの定義として無限次数と無限個の項を許したならば、F
は(主張)の反例になる。