モナドはMonad
以外にもある
この記事では、「モナド」と「Monad
」とを違う意味の言葉として使いわけます。
モナド (圏論)のうち、
ある特殊な場合がMonad
である、とします。
詳しい説明はここではしませんが、
次のFMonad
は、「モナドだけどMonad
じゃない」
性質を表す型クラスです。
-- | Natural
type (~>) f g = forall x. f x -> g x
-- | Functor on Functors
class (forall g. Functor g => Functor (ff g)) => FFunctor ff where
ffmap :: (Functor g, Functor h) => (g ~> h) -> (ff g ~> ff h)
-- | Monad on Functors
class FFunctor ff => FMonad ff where
fpure :: (Functor g) => g ~> ff g
fjoin :: (Functor g) => ff (ff g) ~> ff g
FFunctor
とFMonad
は、
Functor
とMonad
によく似ていることがわかります。
さらに、FFunctor
則とFMonad
則もFunctor
、Monad
のものにそっくりです。
これらをまとめて抽象化できるのが、一般的なほうの「モナド」です。
FFunctor laws:
ffmap id = id
ffmap f . ffmap g = ffmap (f . g)
FMonad laws:
[fpure is natural in g]
∀(n :: g ~> h). ffmap n . fpure = fpure . n
[fjoin is natural in g]
∀(n :: g ~> h). ffmap n . fjoin = fjoin . ffmap (ffmap n)
[Left unit]
fjoin . fpure = id
[Right unit]
fjoin . fmap fpure = id
[Associativity]
fjoin . fjoin = fjoin . ffmap fjoin
具体例1:ReaderT e
ReaderT e
はFFunctor
かつFMonad
になります。
newtype ReadeT e m a = ReaderT { runReaderT :: e -> m a }
instance Functor m => Functor (ReaderT e m)
instance Applicative m => Applicative (ReaderT e m)
instance Monad m => Monad (ReaderT e m)
これが、次のようにFFunctor, FMonad
になります。
instance FFunctor (ReaderT e) where
ffmap :: (Functor f, Functor g) => (f ~> g) -> (ReaderT e f ~> ReaderT e g)
{- ffmap :: (Functor f, Functor g)
=> (forall x. f x -> g x) -> (forall y. ReaderT e f y -> ReaderT e g y) -}
= ReaderT . fmap fg . runReaderT
ffmap fg my
instance FMonad (ReaderT e) where
fpure :: (Functor f) => f ~> ReaderT e f
{- fpure :: f x -> ReaderT e f x -}
= ReaderT . (return :: f x -> (e -> f x))
fpure
fjoin :: (Functor f) => ReaderT e (ReaderT e f) ~> ReaderT e f
= ReaderT . (join :: (e -> e -> f x) -> e -> f x) . fmap runReaderT . runReaderT fjoin
ReaderT e f x
は、newtype
を外せば e -> f x
で、これは
Monad
である(e -> _)
と、Monad
とは限らない f
の合成Functorと見ることができます。
m ~ ((->) e)
とおけば、fpure
はreturn
でf x
を包んでm (f x)
にして、
fjoin
はjoin
でm (m (f x))
を潰してm (f x)
にしているだけです。
一般化すれば、Functorの合成(Data.Functor.Compose)に対して、次のインスタンスが作れます。
instance Functor m => FFunctor (Compose m)
instance Monad m => FMonad (Compose m)
具体例2:WriterT w
WriterT w
もFFunctor
かつFMonad
になります。
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
instance Functor m => Functor (WriterT e m)
instance Applicative m => Applicative (WriterT e m)
instance Monad m => Monad (WriterT e m)
instance FFunctor (WriterT w) where
= WriterT . fg . runWriterT
ffmap fg
instance (Monoid w) => FMonad (WriterT w) where
fpure :: (Functor f) => f ~> WriterT w f
= WriterT . fmap returnWriter
fpure where
= (x, mempty)
returnWriter x -- return x = Writer (x, mempty) :: Writer w x
fjoin :: (Functor f) => WriterT w (WriterT w f) ~> WriterT w f
= WriterT . fmap joinWriter . runWriterT . runWriterT
fjoin where
= (x, w1 <> w2)
joinWriter ((x,w2),w1) -- join (Writer (Writer (x, w2), w1)) = Writer (x, w1 <> w2)
1つめの例と同じように、これは任意のMonad m
に拡張できて、
内側にMonad m
を合成する(`Compose` m)
は、
FMonad
になります。
ただし、Haskellは(`Compose` m)
のような型を作れないので、
それ用の新しい型を定義する必要があります。
newtype FlipCompose m f a = FlipCompose { getFlipCompose :: f (m a) }
instance (Functor m, Functor f) => Functor (FlipCompose m f)
instance (Functor m) => FFunctor (FlipCompose m)
instance (Monad m) => FMonad (FlipCompose m)
具体例3:Sum f
これまでMonad Transformerばかりでしたが、FMonad
がそうしたものに限られるわけではありません。
Sum f
(Data.Functor.Sum)はFMonad
です。これは、Monad Transformerでないどころか、Monad
とは何の関係もありません。
data Sum f g a = InL (f a) | InR (g a)
instance (Functor f, Functor g) => Functor (Sum f g)
そのFMonad
のインスタンスは、Either
のMonad
インスタンスとほぼ同じです。
instance (Functor f) => FFunctor (Sum f) where
ffmap :: (Functor g, Functor h) => (g ~> h) -> (Sum f g ~> Sum f h)
InL fx) = InL fx
ffmap _ (InR gx) = InR (gh gx)
ffmap gh (
instance (Functor f) => FMonad (Sum f) where
fpure :: (Functor g) => g ~> Sum f g
= InR
fpure
fjoin :: (Functor g) => Sum f (Sum f g) ~> Sum f g
InL fx) = InL fx
fjoin (InR (InL fx)) = InL fx
fjoin (InR (InR gx)) = InR gx fjoin (
具体例4:Free f
Free Monad(Free)はFMonad
です。
data Free f a = Pure a | Free (f (Free f a))
instance (Functor f) => Functor (Free f)
instance (Functor f) => Applicative (Free f)
instance (Functor f) => Monad (Free f) where
return = Pure
Pure a >>= k = k a
Free fma >>= k = Free (fmap (>>= k) fma)
instance FFunctor Free where
= hoistFree
ffmap
instance FMonad Free where
= liftF
fpure = foldFree id
fjoin
-- これらはControl.Monad.Freeにある関数ですが、
-- 参照しやすさのためにここにコピペします
-- | The very definition of a free monad is that given a natural transformation you get a monad homomorphism.
foldFree :: Monad m => (forall x . f x -> m x) -> Free f a -> m a
Pure a) = return a
foldFree _ (Free as) = f as >>= foldFree f
foldFree f (
hoistFree :: (Functor g) => (f ~> g) -> (Free f ~> Free g)
= go
hoistFree fg where
Pure x) = Pure x
go (Free fmx) = Free (fmap go (fg fmx))
go (
liftF :: (Functor f) => f ~> Free f
= Free (fmap Pure fx) liftF fx
ここまでの例では省略してきましたが、FFunctor
則・FMonad
則も証明していきたいと思います。
その前に、次の有用な事実を確認しておきます: 任意のFunctor f, Monad n
およびt :: f ~> n
に対して、
foldFree t . liftF = t
foldFree t
はMonad
準同型- 条件1.と2.を満たすような
Monad
準同型はfoldFree t
ただ一つだけ存在する
ここで、ft
がMonad
準同型であるとは、次の条件を満たすことをいいます。
ft . return = ft
ft . join = join . fmap ft . ft
あるいは、これと同値な
$ do x <- mx ft k x= do x <- ft mx ft (k x)
foldFree t . liftF = t
これは定義を展開していけばすぐです。
. liftF
foldFree t = foldFree t . Free . fmap Pure
= (>>= foldFree t) . t . fmap Pure
= (>>= foldFree t) . fmap Pure . t
= (>>= foldFree t . Pure) . t
= (>>= return) . t
= t
foldFree t
はMonad
準同型
. return
foldFree t = foldFree t . Pure
= return
-- 次式を示したい
. join = join . fmap (foldFree t) . foldFree t
foldFree t
= foldFree t $ join mma
lhs mma = join . foldFree t . foldFree t $ mma
rhs mma
lhs mma= case mma of
Pure ma ->
$ join (Pure ma)
foldFree t = t ma
Free fmma ->
$ join (Free fmma)
foldFree t = foldFree t $ Free (fmap join fmma)
= t (fmap join fmma) >>= foldFree t
= join . fmap (foldFree t) . t (fmap join fmma)
= join . fmap (foldFree t) . fmap join $ t fmma
= join . fmap (foldFree t . join) $ t fmma
= join . fmap lhs $ t fmma
= case mma of
Pure ma -> t ma
Free fmma -> join . fmap lhs $ t fmma
rhs mma= case mma of
Pure ma ->
. fmap (foldFree t) . foldFree t $ Pure ma
join = join . fmap (foldFree t) $ return ma
= join $ return (t ma)
= t ma
Free fmma ->
. fmap (foldFree t) . foldFree t $ Free fmma
join = join . fmap (foldFree t) $ t fmma >>= foldFree t
= join . fmap (foldFree t) . join . fmap (foldFree t) $ t fmma
= join . join . fmap (fmap (foldFree t)) . fmap (foldFree t) $ t fmma
= join . fmap join . fmap (fmap (foldFree t)) . fmap foldFree t $ t fmma
= join . fmap (join . fmap (foldFree t) . foldFree t) $ t fmma
= join . fmap rhs $ t fmma
= case mma of
Pure ma -> t ma
Free fmma -> join . fmap rhs $ t fmma
-- よって、再帰的に、lhs = rhsでなければならない
条件1.と2.を満たすようなMonad
準同型はただ一つだけ存在する
言い換えれば、任意のMonad
準同型ft :: Free f ~> n
が
ft . liftF = t
を満たすなら、必ずft = foldFree t = foldFree (ft . liftF)
となります。
これは2段階に分けて証明しましょう。まず、u :: n ~> n'
をMonad
準同型としたとき、
任意のt :: f ~> n
についてfoldFree (u . t) = u . foldFree t
です。これは「foldFree
の自然性」
と呼ぶことにします。
= foldFree (u . t)
lhs = u . foldFree t
rhs
Pure a)
lhs (= return a
Pure a)
rhs (= u (return a :: n a) :: n' a
= return a
Free fma)
lhs (= foldFree (u . t) $ Free fma
= (u . t) fma >>= foldFree (u . t)
= u (t fma) >>= lhs
Free fma)
rhs (= u . foldFree t $ Free fma
= u $ t fma >>= foldFree t
= u (t fma) >>= u . foldFree t
= u (t fma) >>= rhs
また、foldFree liftF = id
です。
$ Pure a
foldFree liftF = return a
= Pure a
$ Free fma
foldFree liftF = liftF fma >>= foldFree liftF
= Free (fmap Pure) fma >>= foldFree liftF
= Free (fmap (>>= foldFree liftF) . fmap Pure $ fma)
= Free (fmap ((>>= foldFree liftF) . Pure) $ fma)
= Free (fmap (>>= return) fma)
= Free fma
したがって
. liftF)
foldFree (ft -- ft はMonad準同型 Free f ~> n より、foldFreeの自然性から
= ft . foldFree liftF
-- foldFree liftF = id
= ft
これらの事実を組み合わせれば、FFunctor, FMonad
則を導くことができます。
-- 以下は定義から容易に確認できる
= liftF
fpure = retract = foldFree id
fjoin
-- 次の式は直接証明することもできるが、
= foldFree (liftF . f)
ffmap f -- より簡単に示せる以下の式
. liftF
ffmap f = ffmap f . Free . fmap Pure
= Free . f . fmap (ffmap f) . fmap Pure
= Free . fmap (ffmap f . Pure) . f
= Free . fmap Pure . f
= liftF . f
-- およびffmapがMonad準同型であることから、`foldFree`の唯一性より
= foldFree (ffmap f . liftF)
ffmap f = foldFree (liftF . f)
FFunctor則]
[id = foldFree (liftF . id) = id
ffmap . ffmap g
ffmap f = foldFree (liftF . f) . foldFree (liftF . g)
-- foldFreeの自然性(foldFree _ 自身もMonad準同型であることに注意)
= foldFree (foldFree (liftF . f) . liftF . g)
-- foldFree t . liftF = t
= foldFree (liftF . f . g)
= foldFree (liftF . (f . g))
= ffmap (f . g)
FMonad則 自然性]
[-- fpure = liftFなので、これはすでに示している
. fpure = fpure . f
ffmap f
. fjoin
ffmap f = ffmap f . foldFree id
= foldFree (ffmap f . id)
= foldFree (ffmap f)
. ffmap (ffmap f)
fjoin = foldFree id . foldFree (liftF . ffmap f)
= foldFree (foldFree id . liftF . ffmap f)
= foldFree (ffmap f)
FMonad則Left Unit]
[. fpure
fjoin = foldFree id . liftF
-- foldFree t . liftF = t
= id
FMonad則Right Unit]
[. ffmap fpure
fjoin = foldFree id . foldFree (liftF . liftF)
-- foldFreeの自然性
= foldFree (foldFree id . liftF . liftF)
-- ^^^^^^^^^^^^^^^^^^^
= foldFree (id . liftF)
= foldFree liftF
= id
FMonad則Associativity]
[. fjoin
fjoin = foldFree id . foldFree id
= foldFree (foldFree id . id)
= foldFree (foldFree id)
. ffmap fjoin
fjoin = foldFree id . foldFree (liftF . foldFree id)
= foldFree (foldFree id . liftF . foldFree id)
-- ^^^^^^^^^^^^^^^^^^^
= foldFree (id . foldFree id)
= foldFree (foldFree id)
なんでこの記事を書いたのか?
このFFunctor
やFMonad
は、
じつは私がついこの間書いたものです。Redditでのこの話題に対する反応なので、別に実用上の必要があったわけではありません。一応Hackageも漁りましたけど、category-extrasに存在したことがあるぐらいで、
今もメンテナンスされているパッケージには見つかりませんでした。多分他の人も必要なかったのか、自分の検索が足りてないかです。
ただ、割とよく似ているものにindex-coreとmmorphとがあります。それぞれ、
- index-core: カインド
k -> Type
をもつ任意の型コンストラクタf :: k -> Type
上の関手やモナド- パッケージの元ネタ論文: Kleisli arrows of outrageous fortune(pdf)
- モナドは
IMonad
という名前
- mmorph:
Monad
上の関手やモナドMonad
準同型だけを考える- モナドは
MMonad
という名前
です。FMonad
はFunctor
上のモナドなので、上のどちらとも一般/特殊の関係にありません。しかも、例に挙げたように、
FFunctor
、FMonad
のインスタンスは多様で、かつたびたび使う型が含まれています。
ちょっと深堀りしてみる価値があるかな、と思ってやってみました。
次の表はIMonad
、MMonad
、FMonad
の比較です。
Instance | IMonad |
MMonad |
FMonad |
|
---|---|---|---|---|
ReaderT e |
✔ | ✔ | ✔ | |
Monoid w => |
WriterT w |
✔ | ✔ | |
Monad f => |
Compose f |
✔ | ✔ | |
Sum f |
✔ | ✔ | ||
Free |
✔ | |||
Functor f => |
FreeT f |
✔ [1] | ||
IxState [2] |
✔ |
[1] mmorphパッケージには無いけれど多分なれます。
また、MMonad (FreeT f)
とf
を固定してm
を引数と見るのでなく、m
を固定してf
を引数と見るなら、FreeT _ m
はFMonad
です。
[2] IxState: 下記のもの。s
が反変な位置にあるのでFMonad
になれません。
あと、x
として、GADTを使ったものを入れないと、
計算後の状態の型がわからなくなるので、Functor
なx
だけを使うとかなり不便です。
data SomePair x where
SomePair :: t -> x t -> SomePair x
data IxState x s where
IxState :: (s -> SomePair x) -> IxState x s