liftMを学ぶ(モナドに関数を適用する)

liftM の使い方が分からなかったので調べてみました。

ghci> :m +  Control.Monad
ghci> :t liftM
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r

liftM は

モナドの r を返す。
つまり、a1 を引数に r を帰す普通の関数をモナドにも適用出来るようにするもの、って理解で良いかな。

liftM関連 - おっぱい大魔神の日記」の例が分かりやすい。

import Control.Monad

str = return "Haskell"::Maybe String
main = print $ liftM length str

length はリストを引数にリストの長さを返す関数ですから、以下のようにそのままではMaybe型の値に摘要出来ません。Nothing のときの場合分けとJust を外す操作が必要です。

length :: [a] -> Int
len = case str of
          Nothing -> Nothing 
          Just s  -> Just (length s)

liftM を使えばMaybe型のリストの長さを返し、リストが Nothing だったら、Nothing を返します。

> liftM length $ liftM (++".world!") $ Just "Hello" --=> Just 12
> liftM length $ liftM (++".world!") Nothing        --=> Nothing

> map (liftM length) [Nothing, Just "hello,world", Just ""]
  --=> [Nothing,Just 11,Just 0]
>  len <- liftM length getLine :: IO Int
Hello,world!
> len --=> 12

Hoogle / liftM:lengthは引数がひとつですが、摘要する関数の引数に応じたliftMがあるようです。

  • liftM のソース。

liftM のソースを見てみましょう。do 記法です。モナドm1からx1を取り出し、x1 に引数で受け取った関数 f を摘要し、 return でモナドにくるんで返しています。

-- | Promote a function to a monad.
liftM   :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1              = do { x1 <- m1; return (f x1) }