lifted applicative style

モナドを使った演算を書いている時は、applicative styleを使うと記述量を少なくできて便利だ。

>>> (++) <$> getLine <*> getLine
hoge
foobar
"hogefoobar"

使う関数が最終的にモナドを返す場合、最後にjoinしてやればいい。

>>> let catPut a b = putStrLn ("== " ++ a ++ b)
>>> join $ catPut <$> getLine <*> getLine
hoge
foobar
== hogefoobar

さて、上記と同様のことをmonad transformerで拡張した文脈中ではどうするか、ちょっと悩んでしまった。

正解は(lift =<<)を使えばいい。

>>> let action = lift =<< catPut <$> ask <*> lift getLine
>>> runReaderT action "hoge"
foobar
== hogefoobar

(lift =<<)の型はこうだ。

>>> :t (lift =<<)
(lift =<<)
  :: (Monad (t m), Monad m, MonadTrans t) => t m (m b) -> t m b

見ての通り、join :: Monad m => m (m b) -> m b によく似ている。

ちなみにcatPutへの引数がモナドではない素のデータだとこうなる。

>>> let action' = lift $ catPut "HOGE" "FOOBAR" :: ReaderT String IO ()
>>> runReaderT action' "hoge"
== HOGEFOOBAR

素のデータの場合の記法から、($)を(=<<)に、関数適用を(<$>)と(<*>)に書き換えればapplicative styleになる。