実は関数もモナドとして捉えることができる
関数(a->b)はそのままArrowのインスタンスになっているわけなんですが、実はモナドのインスタンスとしても機能できることに気づきました…
ステートモナドとかを見れば何の秘密にもなっていないことなんですが…「関数もモナドのインスタンスとして扱える」という認識が今まではっきりとはなかったのでちょっとインパクトがありました…
ということでやってみました:
newtype MonadF i o = MonadF {runMF :: i -> o} instance Monad (MonadF i) where return a = MonadF $ const a (MonadF af) >>= b = MonadF $ \i -> (runMF $ b $ af i) i instance Functor (MonadF i) where fmap f (MonadF mf) = MonadF $ \i -> (f . mf) i instance Applicative (MonadF i) where pure = return f <*> b = f >>= \f' -> b >>= \b' -> return $ f' b'
ついでにFunctorとApplicativeのインスタンスにもしちゃいました。ステートモナドの例に倣って関数のパラメタを隠蔽しちゃうことができるんですね…
ちなみにMonadF自身は(a->b)な関数をモナド化します。面白いのはバインド(>>=)の右辺にくるやつ(Kleisli関数?)のタイプが(a -> MonadF b c)つまり(b -> a -> c)という型の関数になるわけですね…
ちなみに使ってみるとこんな感じ:
testMF = (MonadF (+1)) >>= \j -> (MonadF (* j)) main = do print $ (runMF testMF) 2 print "is equivalent of" print $ (\i -> (i + 1) * i) 2
ちゃんとバインドを使ってMonadF同士を結合できてますね。testMFのなかではパラメタが隠蔽されてますね…
いやー、いったい何がうれしいんでしょうか?教えてください。
ではでは。
と、ここまで書いてわかったのですが、実はこれ、Readerモナドというものとそっくりですね…Readerのほうは((->) r)という形で関数がインスタンスに取り込まれています。ちなみにReaderはMonadReaderのインスタンスで、MonadReader経由でlocalというメソッドが提供されています。これは暗黙変数に関数を適用した結果のをつかってモナドを実行することができるらしいです…ライブラリにも定義されているので便利なときもあるんでしょうね…