Haskellモナドトランスフォーマー(6)
Maybeトランスフォーマーのデータ型を宣言するところから始めましょう。MaybeTコンストラクタはひとつ引数を取ります。トランスフォーマーは、元のモナドと同じデータを取るので、newtypeキーワードを使用することにします。dataキーワードを使ってもいいのですが、必要のない負荷がかかるのでやめておきましょう。
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
最初は戸惑うかもしれませんが、見た目ほど複雑でもないです。MaybeTのコンストラクタは、型m (Maybe a)である値をひとつ引数に取ります。ただそれだけです。このレコードの部分はただの糖衣構文で、MaybeTがレコードのように見ることができるというのと、runMaybeTを呼ぶことで引数にとった値を取得できます。
わかりやすく考えるなら、モナドトランスフォーマーをサンドイッチとして考えましょう。サンドイッチの下が元のモナド(ここではMaybe)、中身が「内なるモナド」m、そして一番上がモナドトランスフォーマーMaybeTです。関数runMaybeTの存在意義は、この一番上のモナドトランスフォーマーMaybeTを取り除くことです。runMaybeTの型は(MaybeT m a) -> m (Maybe a)です。
改めて言いますが、モナドトランスフォーマー自身もモナドです。MaybeTモナドの実装の一部が下にあります。この実装を見れば、元のモナドであるMaybeがいかにシンプルな実装であるかがわかりと思います。
Maybe |
---|
instance Monad Maybe where b_ v >>= f = case b_v of Nothing -> Nothing Just v -> f v
MaybeT |
---|
instance (Monad m) => Monad (MaybeT m) where tmb_v >>= f = MaybeT $ runMaybeT tmb_v >>= ¥b_v -> case b_v of Nothing -> return Nothing Just v -> runMaybeT $ f v
MaybeTの実装がMaybeのbindの実装に似ていると気づくかもしれません(ただし例外処理多し)。この余分な実装がモナドのサンドイッチから2つの層を取り出すために必要な部分です。