Maybe
はFree
モナドの一つです。特に、Maybe = Free Proxy
です。
data Maybe a = Nothing | Just a
data Free f a = Pure a | Wrap (f (Free f a))
data Proxy a = Proxy
-- 自然変換の矢印
type (~>) f g = forall x. f x -> g x
maybeToFree :: Maybe ~> Free Proxy
Nothing = Wrap Proxy
maybeToFree Just a) = Pure a
maybeToFree (
freeToMaybe :: Free Proxy ~> Maybe
Pure a) = Just a
freeToMaybe (Wrap _proxy) = Nothing freeToMaybe (
これは単にFunctor
として同型であるだけでなく、Monad
としても同型になっています1。
(そもそも、Maybe
と同型なFunctor
に定義できるMonad
のインスタンスは一つしかありません。)
ここまでならば「ちょっと面白い」というだけなのですが、Free
モナドというのはとても特別なモナドです。
Free
モナドの普遍性というものがあるからです。
どんなモナド
m
についても、モナド準同型tr :: Free f ~> m
と自然変換h :: f ~> m
は、 以下のfoldFree
によって一対一に対応するfoldFree :: Monad m => (f ~> m) -> (Free f ~> m) Pure a) = pure a foldFree _ (Wrap fmx) = h fmx >>= foldFree h foldFree h (
Maybe
はFree Proxy
に同型であることを使えば、“Maybe
モナドの普遍性”を以下のように得られます。
どんなモナド
m
についても、モナド準同型tr :: Maybe ~> m
と自然変換h :: Proxy ~> m
は、 以下のfoldMaybe
によって一対一に対応するfoldMaybe :: Monad m => (Proxy ~> m) -> (Maybe ~> m) Just a) = pure a foldMaybe _ (Nothing = h Proxy foldMaybe h
特に、モナド準同型 tr :: Maybe ~> m
は
tr Nothing :: ∀x. m x
のみによって特徴付けられることがわかります。
また、Maybe = Free Proxy
なのであれば、FreeT Proxy m
は何でしょうか?
うれしいことに、それはMaybeT m
と一致します2。
newtype MaybeT m a = MaybeT (m (Maybe a))
data FreeF f a b = PureF a | WrapF (f b)
newtype FreeT f m a = FreeT (m (FreeF f a (FreeT f m a)))
maybeT_freeT :: ∀m. Monad m => MaybeT m ~> FreeT Proxy m
MaybeT x) = FreeT (maybeToFreeF <$> x)
maybeT_freeT (where maybeToFreeF (Just x) = PureF x
Nothing = WrapF Proxy
maybeToFreeF
freeT_maybeT :: ∀m a. Monad m => FreeT Proxy m ~> MaybeT m
FreeT x) = MaybeT (freeFToMaybe <$> x)
freeT_maybeT (where freeFToMaybe (PureF x) = Just x
WrapF Proxy) = Nothing freeFToMaybe (
FreeT
にも普遍性があります。
m, n
をモナドとする。任意のモナド準同型tr :: FreeT f m ~> n
は、 自然変換f :: f ~> n
とモナド準同型tr2 :: m ~> n
の組(f, tr2)
へ、 以下のmergeFreeT
によって一対一に対応する。mergeFreeT :: Monad n => (f ~> n) -> (m ~> n) -> (FreeT f m ~> n) = go mergeFreeT f tr2 where FreeT mx) = tr2 mx >>= goF go ( PureF a) = pure a goF (WrapF fy) = f fy >>= go goF (
これも”MaybeT
モナドの普遍性”に翻訳してみましょう。
m, n
をモナドとする。任意のモナド準同型tr :: MaybeT m ~> n
は、 自然変換h :: Proxy ~> n
とモナド準同型tr2 :: m ~> n
の組(f, tr2)
へ、 以下のmergeMaybeT
によって一対一に対応する。mergeMaybeT :: Monad n => (Proxy ~> n) -> (m ~> n) -> (MaybeT m ~> n) = go mergeMaybeT h tr2 where MaybeT mx) = tr2 mx >>= maybe (h Proxy) pure go (
これは結構意外なことではないでしょうか?
例えば m a = Reader k a = k -> a
とすると、モナド準同型tr
とは自然変換
tr :: MaybeT (Reader k) ~> n
-- ~ ∀a. (k -> Maybe a) -> n a
であって、モナド演算を保つという条件を満たすものです。
(k -> Maybe a)
という関手は非常に”大きい”関手で、
仮に単独のdata F a = ...
で表そうとするならば、
2^|k|
個のコンストラクタを設けないといけません。
そのため、この”大きな”関手から別の関手n
への自然変換は、n
によっては本当に凄まじい数が存在します。
ですが、もしその自然変換がモナド準同型であると知っているならば、
モナド準同型tr2 :: ∀x. (k -> x) -> n x
とただ一つの値h Proxy :: ∀x. n x
だけで記述できることがわかったのです。
凄いでしょう!