人気ブログランキング | 話題のタグを見る

liftM と lift

liftM はモナドの関数、lift はモナド変換子の関数だ。何かを持ち上げるという意味なのだろうが使い方がさっぱりわからない。こんなときは、単純な使い方を試してみるに限る。liftM と lift を試して見るためには、Control.Monad.State モジュールをインポートすると両方使える。

Prelude> import Control.Monad.State

まず liftM からだ。最初は関数の型を見てみる。

Prelude Control.Monad.State> :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r

m はモナドを表すので、これは a1 -> r 型の関数から m a1 -> m r 型の関数に変換されることがわかる。そこで、次のように foo という関数を作って試してみた。

Prelude Control.Monad.State> foo = liftM (*2)
Prelude Control.Monad.State> :t foo
foo :: (Monad m, Num r) => m r -> m r

(*2) は数を2倍にする関数だから foo = liftM (*2) は m x の x の値を2倍にして m 2x を返す関数になる。

Prelude Control.Monad.State> foo (Just 2)
Just 4

liftM の動作は意外と単純だった。こんどは lift に挑戦してみる。lift の説明はいろいろ読んでもよくわからなかったので、最も単純な例を試してみた。runStateT の引数に普通にモナドプログラムを渡すと、単に State モナドとして働く。

Prelude Control.Monad.State> runStateT (return 2) ()
(2,())

ところが、lift に Just 2 というMaybe モナドを渡すと、(2,()) というrunState のペアが Just にラッピングされて返されてくる。

Prelude Control.Monad.State> runStateT (lift (Just 2)) ()
Just (2,())

State モナドの中に Maybe モナドの値を放り込んだら、裏表が逆になって Just にラッピングされて戻されてきた。なんだかよくわからないが、ともかくState モナドのプログラムの中で Maybe モナドが使えることがわかる。そこで、もう少し State モナドらしいことをさせてみよう。

Prelude Control.Monad.State> runStateT (do x <- get; y <- lift (Just 3); return (x+y)) 7
Just (10,7)

状態の 7 と Just 3 の 3 が足し算されて値として戻されているが、そのペア (10, 7) は Just にラッピングされて戻ってくる。State モナドのプログラミングのなかで Maybe モナドの Just 3 の 3 を利用することができるが、その場合出力が Just (10,7) のように Maybe モナドになってしまっている。

しかし、lift の引数が IO モナドだとそのありがたみがもっとわかる。

Prelude Control.Monad.State> runStateT (do x <- get; y <- lift(getLine); return (x ++ y)) "hello, "
world
("hello, world","hello, ")

出力にモナドのデータコンストラクタが表示されていないがこれは出力が IO モナドだからだ。

Prelude Control.Monad.State> :t runStateT (do x <- get; y <- lift(getLine); return (x ++ y)) "hello, "
runStateT (do x <- get; y <- lift(getLine); return (x ++ y)) "hello, "
:: IO ([Char], [Char])

このことは、StateTモナド変換子を使うことで、IOモナドのプログラムに状態を導入することができることを示している。つまり、2つのモナドを一緒に使うことができるのだ。これで StateT モナドの使い方がわかったわけではないが、少なくとも IO モナドのプログラムの中に State モナドによる状態を導入できることがわかったので、StateT モナド変換子をいろいろ試してみることができる気がしてきた。


by tnomura9 | 2018-11-20 00:03 | Haskell | Comments(0)
<< 代数的データ型の再帰的定義 snoc >>