概要
A
を適当な型とします。
MaybeT (Reader A) x
とReaderT A Maybe x
という2つの型は、
newtype
の包みを剥がせばどちらもA -> Maybe x
となり、
更にこれらは実質的に同じモナドを定義しています。
この記事ではこれらのモナド(Reader A, Maybe, MaybeT (Reader A)
)に何度も言及するので、
以下のように省略して書くことにしましょう。
type P = Maybe -- Pointed の P
type R x = A -> x -- Reader の R
-- R = Reader A
type RP x = A -> Maybe x
-- RP ~ R ∘ P
-- ~ ReaderT A Maybe
-- ~ MaybeT (Reader A)
また、f ~> g
という表記で自然変換∀x. f x -> g x
を表すことにします。
type (~>) f g = forall x. f x -> g x
このモナドRP
は以下の3つの特別な性質を持ちます。
Monad
とMonad
準同型からなる圏において、R
とP
の余積(後述;Either
のようなもの)とみなせる対象である- 標準入射(
Either
に対するLeft
やRight
のような射)となるモナド準同型liftP :: P ~> RP
とliftR :: R ~> RP
がある - 任意のモナド
T
と2つのモナド準同型r :: R ~> T
とp :: P ~> T
が与えられたとき、 普遍射(Either
に対するeither f g
のような射)となるモナド準同型coprod r p :: RP ~> T
をもつ
- 標準入射(
r
が単射ならばcoprod r p
も単射であるr
がEquifibered自然変換ならばcoprod r p
もEquifibered自然変換である- Equifibered(nLab、日本語の訳語はわかりません)については次回の記事をお待ちください
これを使うことで、過去の記事 においては力業で行った証明を、見通しよくすることができます。
MaybeT m
は Maybe
と m
の余積である
RP = MaybeT (Reader A)
はMaybe
とReader A
の余積です。
より一般に、任意のモナドm
について、MaybeT m
はMaybe
とm
の余積になります。
モナドの余積という言葉について、明確に定義しておきます。
あるモナドLM
がモナドL
とモナドM
の余積であることを、
次のように定義します。
L, M
のそれぞれからLM
へのモナド準同型をもつ。lift1 :: L ~> LM lift2 :: M ~> LM
これらの準同型は標準入射とよぶ。
任意のモナド
N
と、L, M
それぞれからN
へのモナド準同型tr1 :: L ~> N, tr2 :: M ~> N
が与えられたとき、 以下の性質を満たす唯一のモナド準同型coprod tr1 tr2 :: LM x ~> N
が存在する。coprod :: Monad n => (L ~> n) -> (M ~> n) -> (LM ~> n) . lift1 = tr1 coprod tr1 tr2 . lift2 = tr1 coprod tr1 tr2
モナドL
とM
の余積は、存在すればいずれも同型なモナドになります1。
MaybeT m
がMaybe
とm
の余積であることは、先日の記事において説明した
Free
とFreeT
の普遍性Maybe
がFree Proxy
と同型であるMaybeT m
がFreeT Proxy m
と同型である
から得られます。Free
とFreeT
の普遍性はそれぞれ
Free
の普遍性-
Free f
からモナドT
への準同型tr :: Free f x -> T x
は、 自然変換h :: f x -> T x
と一対一に対応する FreeT
の普遍性-
FreeT f m
からモナドT
への準同型tr :: FreeT f m x -> T x
は、 準同型tr1 :: m x -> T x
と自然変換h :: f x -> T x
との組(tr1, h)
と一対一に対応する
でしたが、ここでFreeT
の普遍性に対してFree
の普遍性を使うと
FreeT
の普遍性(余積ver.)-
FreeT f m
はm
とFree f
との余積である。 すなわち、FreeT f m
からモナドT
への準同型tr :: FreeT f m x -> T x
は、 準同型tr1 :: m x -> T x
と準同型tr2 :: Free f x -> T x
の組(tr1, tr2)
と一対一に対応する
と書くことができます。前回の記事で述べたように、
モナドの同型Maybe ~ Free Proxy
とMaybeT ~ FreeT Proxy
があるため、
MaybeT m
はMaybe
とm
の余積であることがわかります。
coprod
の実装も、ここで明示的に書いておきます。
coprod :: (Monad m, Monad t)
=> (∀x. m x -> t x)
-> (∀x. Maybe x -> t x)
-> m (Maybe x) -> t x -- MaybeT m x ~ m (Maybe x)
= join . fmap tr2 . tr1 coprod tr1 tr2
r
が単射ならばcoprod r p
も単射である
ふたたび、RP = MaybeT (Reader A)
モナドに限った議論をします。
T
を任意のモナドとして、モナド準同型r, p
が以下のように与えられているとします。
r :: R x -> T x
p :: P x -> T x
RP
はR = Reader A
とP = Maybe
の余積でしたから、以下のモナド準同型があります。
rp :: RP x -> T x
= coprod r p
rp = join . fmap p . r
このとき、r
が単射2ならば、rp = coprod r p
も単射であることがいえます。
モナド準同型p :: P x -> T x
は、T
が自明なモナド(Data.Proxy)
でない限り常に単射です。
したがって、「r
が単射ならばrp
も単射」は、「r, p
がともに単射ならばrp
も単射」と、
自明なケースを除いて同じことを言っています。
これは非常に珍しいことであり、一般の余積について言えることではありません。
例えば、Hask
における余積Either
が持つ普遍射either :: (x -> z) -> (y -> z) -> Either x y -> z
においては、
f,g
がそれぞれ単射であってもeither f g
が単射であるとは限りません。
更に、MaybeT m
に限っても、任意のモナドm
について成り立つわけではありません。
反例をひとつあげると、id :: Maybe x -> Maybe x
は明らかに単射なモナド準同型ですが、
coprod id id :: MaybeT Maybe x -> Maybe x
は単射ではありません。
実際、MyabeT Nothing
とMaybeT (Just Nothing)
は、どちらもNothing
に写されます。
ほかの圏で同じことが起きる例には、「アーベル群の圏における ℤ/cℤ と ℤ/dℤ の余積(=直積) ℤ/cℤ ⊕ ℤ/dℤ でcとdが互いに素なとき」などがあります。
証明の補助のため、以下の2つの関数を定義します。
zero :: T a
= p Nothing
zero
refill :: T a -> R Bool -> T (P a)
= join . r $ \i ->
refill ta cond if cond i then fmap Just ta else pure Nothing
-- 型シノニムに注意
-- P = Maybe
-- R x = A -> x
mask :: T a -> R Bool -> T a
= join . fmap p $ refill ta cond mask ta cond
x
を任意のRP a
型の値としたとき、refill (rp x) cond
を以下のように計算することができます。
- (Eq1)
-
= refill (rp x) cond $ \i -> if cond i then fmap Just (x i) else Just Nothing rp
証明
refill (rp x) cond= join . r $ \i ->
if cond i then fmap @T Just (rp x) else pure @T Nothing
= join . r $ \i ->
if cond i then rp (fmap @RP Just x) else rp (pure @RP Nothing)
= join . r . fmap rp $ \i ->
if cond i then (\j -> fmap Just (x j)) else const (Just Nothing)
= join . r . fmap rp $ \i j ->
if cond i then fmap Just (x j) else Just Nothing
-- 下記 (Eq1') より
= join . fmap p . r . join @R $ \i j ->
if cond i then fmap Just (x j) else Just Nothing
= rp $ \i ->
if cond i then fmap Just (x i) else Just Nothing
-- (Eq1')
. r . fmap rp
join = join . r . fmap (join . r . fmap p)
= join . r . fmap join . fmap r . fmap (fmap p)
= join . fmap join . fmap (fmap p) . r . fmap r
= join . join . fmap (fmap p) . r . fmap r
= join . fmap p . join . r . fmap r
= join . fmap p . r . join @R
同様に、mask (rp x) cond
も計算すると以下のようになります。
- (Eq2)
-
= rp $ \i -> if cond i then x i else Nothing mask (rp x) cond
証明
mask (rp x) cond= join . fmap p $ refill (rp x) cond
= join . fmap p . join . fmap p . r $ \i ->
if cond i then fmap Just (x i) else Just Nothing
-- 下記(Eq2')より
= join . fmap p . fmap (join @P) . r $ \i ->
if cond i then fmap Just (x i) else Just Nothing
= join . fmap p . r $ \i ->
if cond i then join (fmap Just (x i)) else join (Just Nothing)
= rp $ \i -> if cond i then x i else Nothing
-- (Eq2')
. fmap p . join . fmap p
join = join . join . fmap (fmap p) . fmap p
= join . fmap join . fmap (fmap p) . fmap p
= join . fmap (join . fmap p . p)
= join . fmap (p . join @P)
= join . fmap p . fmap (join @P)
(Eq1)を用いてrefill (rp x) (isJust . x)
を計算すると、次の(Eq3)を得ます。
- (Eq3)
-
refill (rp x) (isJust . x) = r x
証明
. x)
refill (rp x) (isJust = join . fmap p . r $ \i ->
if isJust (x i) then fmap Just (x i) else Just Nothing
= join . fmap p . r $ \i ->
case x i of
Just a -> Just (Just a)
Nothing -> Just Nothing
= join . fmap p . r $ \i -> Just (x i)
= join . fmap p . r . fmap Just $ x
= join . fmap (p . Just) . r $ x
= r x
(Eq3)により、次のことがわかります。
- (Lemma1)
rp
によってzero
に写るのはconst Nothing
のみ -
rp x = zero
⇒x = const Nothing
証明
rp x = zero
となるx
に対して、あるi :: A
があってx i = Just _
と仮定すると、
以下の自然変換f :: a -> RP a
が単射になります。
f :: a -> RP a
= fmap (fmap (const a)) x f a
refill (rp (f a)) (isJust . f a) = r (f a)
です。しかし、isJust . f a
はa
に依存しないisJust . x
に等しく、
仮定よりrp (f a)
はa
によらずzero
に等しいためこちらも定数です。したがって、左辺は定数です。
これはr . f
が単射であることと矛盾します。したがってx i = Just _
となるようなi
は存在せず、x = const Nothing
です。
次に、以下の事実を示します。
- (Lemma2)
-
mask (rp x) cond = zero
⇒(\i -> cond i && isJust x i) = const False
証明
(Eq2)より
mask (rp x) cond= rp (\i -> if cond i then x i else Nothing)
= zero
です。したがって、(Lemma1)より
-> if cond i then x i else Nothing) = const Nothing
(\i . (\i -> if cond i then x i else Nothing) = isJust . const Nothing isJust
\i -> cond i && isJust x i
に、右辺はconst False
に等しくなります。
これらを用いれば、refill . rp
が単射であること、結果としてrp
も単射であることを証明できます3。
refill . rp
の単射性-
任意の
x,y :: RP a
に対して、refill (rp x) = refill (rp y)
ならばx = y
証明
mask
の定義より、refill (rp x) = refill (rp y)
ならばmask (rp x) = mask (rp y)
です。
また、(Eq2)より
not . isJust . x)
mask (rp x) (= rp $ \i -> if not (isJust (x i)) then x i else Nothing
= rp $ \i -> Nothing
= zero
です。ここで、仮定よりmask (rp x) = mask (rp y)
なので、(Lemma2)より次式(*)が成り立ちます。
-> not (isJust (x i)) && isJust (y i)) = const False -- (*) (\i
x
とy
を入れ替えて、同様に次式(**)も成り立ちます。
-> not (isJust (y i)) && isJust (x i)) = const False -- (**) (\i
これらが両方成り立つのは、(\i -> isJust (x i) /= isJust (y i)) = const False
のとき、
すなわちisJust . x = isJust . y
のときに限ります。
更に(Eq1)を用いると
= refill (rp x) (isJust . x) = refill (rp y) (isJust . y) = r y r x
r
は単射なので、x = y
でなければなりません。
「rp
が単射である」ことの意義
「r
が単射ならばrp = coprod r p
も単射である」ことがわかりましたが、
これがどのように役立つのか説明していませんでした。
役立つ一例を見てみましょう。
以下に定義するH
に対してMonad H
というインスタンスを定義するならば、
必ずpure a = One a
でなければいけません。
data H a = Err Integer | One a | Two a a
deriving (Functor)
instance Monad H where
pure :: a -> H a
join :: H (H a) -> H a
「r
が単射ならばrp = coprod r p
も単射である」を使えば、このことを容易に示せます。
join . pure = id
が成り立たなくなるため、pure _ = Err i
にはなり得ません。pure a = F2 a a
であるようなMonad H
のインスタンスがあったと仮定して、矛盾を導きます。このとき、モナド則から
join (Two (Two aa ab) (Two ba bb)) = Two aa bb
となります。 ここでr :: (Bool -> a) -> H a
とp :: Maybe a -> H a
を以下のようにおくと、 これらはモナド準同型になり、更にr
は単射です。 したがって、rp :: (Bool -> Maybe a) -> H a
も単射です。= Two (f False) (f True) r f Nothing = Err 0 p Just a) = pure a p (
Bool -> Maybe a
は以下の4つの異なる”コンストラクタ”-> Nothing :: Bool -> Maybe a \_ -> \case { False -> Just a0; True -> Nothing} :: a -> (Bool -> Maybe a) \a0 -> \case { False -> Nothing; True -> Just a1} :: a -> (Bool -> Maybe a) \a1 -> \case { False -> Just a0; True -> Just a1} :: a -> a -> (Bool -> Maybe a) \a0 a1
を持ちます。
rp
が単射であるためには、rp
はこれらをF
の異なるコンストラクタに写さなければ ならず、更に最初の1つ以外をErr _
に写すことができません。ですが、これは不可能です。
しかし、この事実(r
が単射ならばrp
も単射)だけでは力不足な目標もあります。
実は、次のような既知の事実を一挙に証明することを目論んでいました。
リスト関手
[]
に対するMonad
のインスタンスとして、可能なpure :: x -> [x]
の選択肢はpure x = [x]
のみである。定数でない多項式Functor
F
で、“次数”が1の項を持たず、かつ”定数項”を持つようなもの、 すなわちF(x) = c + x^2 * G(x)
と書けるようなものには、Monad
のインスタンスが存在しない。- このブログで以前証明しました(過去の記事)
リスト関手[]
に関する事実は、前述のH
についての証明をなぞれば、
「n
個の値をとる型A
に対してrp :: (A -> Maybe a) -> [a]
が単射であることができない」
といった形で証明できそうに見えます。しかし、リスト関手には「定数でない項」が無限個あるため、
単射(A -> Maybe a) -> [a]
が取れなくなることはありません。2つめの事実も、同様に「単射である」
だけでは不十分です。
既にあるこれらの証明では、F
のあるコンストラクタの”次数”、[]
の”長さ”・・・といったものが丁度 n
である、
という形の制約を発見し、うまく利用しています。しかし、「単射である」という事実からは、“次数”n
のコンストラクタが写される
先のコンストラクタの”次数”はn
以上であるということしかわからず、ぴったり n
であるという条件が得られません。
ここでの「“次数”n
のコンストラクタが写される先のコンストラクタの”次数”がぴったりn
に一致する」
を厳密に述べるための概念がEquifibered自然変換というものです4。
このブログの次回の記事でそれを説明しようと思います。
ここでの余積の定義は、一般の圏における余積をモナドとモナド準同型からなる圏に 特殊化したものです。そのため、一般の圏における「余積は存在すれば同型を除いて一意」 という証明がそのまま通用します。
モナド
L, M
の余積として、モナドLM, LM'
があったとします。 それぞれ標準入射と普遍射の名前をlift1 :: L ~> LM lift2 :: M ~> LM coprod :: Monad n => (L ~> n) -> (M ~> n) -> (LM ~> n) lift1' :: L ~> LM' lift2' :: M ~> LM' coprod' :: Monad n => (L ~> n) -> (M ~> n) -> (LM' ~> n)
とします。このとき
= coprod lift1' lift2' :: LM ~> LM' f = coprod' lift1 lift2 :: LM' ~> LM g
とおくと、普遍性から
f
とg
は互いに逆であることが示せます。↩︎f
が単射である⇔「任意のx,y
に対してf x = f y
⇒x = y
」と定義します。↩︎一般に、
f . g
が単射ならばg
も単射です。 ∵g x = g y
⇒f (g x) = f (g y)
かつf (g x) = f (g y)
⇒x = y
↩︎正確には、「私がこの概念を厳密化したものを探し求めて本やネットを漁っていたら、最近になってようやく見つけた用語」 がEquifibered自然変換です。
なので、「より適切な概念があるよ」などのご指摘がございましたら教えていただけると嬉しいです。 (加えて、どんなコメントでも元より大歓迎です。メール viercc@gmail.com またはtwitter @viercc まで!)↩︎