はじめに

Continuation (継続) について全く勉強したことが無いので Control.Monad.Cont で定義されている ContT とかいつ使うんだろうなーと思っていましたが、ついに利用機会がありました!!!

僕が考えたんじゃなくて fumieval さんに相談して、教えてもらったんですけどね。

問題のコード

YesodHandler ではパラメータの取得するために lookupGetParamlookupPostParam を利用すると思います。

実際にはこんな感じでパラメータを取得していくつか処理を行います。

deleteTestR :: Handler Html
deleteTestR = do
  mParam <- lookupPostParam "key"

  case mParam of
    Nothing -> returnJson $ String "パラメータが不正です。"
    Just param ->
      case textToSqlKey param of
        Nothing -> returnJson $ String "キーが見つかりませんでした。"
        Just key -> do
          mRecord <- runDB $ get key
          case mRecord of
            Nothing -> returnJson $ String "削除対象のデータが見つかりませんでした。"
            Just _  -> returnJson $ String "success"

このコード、どう考えても嫌な感じですよね・・・。ネストやばいし。

do で書くと Maybe 型なので値を返せないし、ベースに Handler モナドがあるので Either で置き換えるのも良くわかんないな・・・。と思って、結構放置してました。

ContT を使ってリファクタリング!

先程のプログラムを ContT で置き換えるとこうなります。

deleteTestR :: Handler Html
deleteTestR = do
  mParam <- lookupPostParam "key"

  evalContT $ do
    param <- mParam !? "パラメータが不正です。"
    key <- textToSqlKey param !? "キーが見つかりませんでした。"
    mRecord <- lift $ runDB $ get key
    deletedBrand <- mRecord !? "削除対象のデータが見つかりませんでした。"
    lift $ returnJson $ String "success"
  where
    Nothing !? e = ContT $ const $ returnJson $ String e
    Just a  !? _ = ContT ($ a)

感動しましたね。継続すごいな!って。

まとめ

継続勉強しよ。