Freeモナド実用の旅(1): ループからのBreak

とても簡単。Free(T)に「続きがない」ということを表すFunctorをはめるだけ。
ex1はある文字列を入力すると終了するプログラム。

import Prelude hiding (break)
import Control.Monad.Trans.Free
import Control.Monad.Trans
import Control.Monad

data Zero a = Zero deriving (Show, Eq, Ord)
instance Functor Zero where
    fmap _ _ = Zero

type BreakT = FreeT Zero

break :: Monad m => BreakT m ()
break = liftF Zero

runBreakT :: Monad m => BreakT m a -> m (Maybe a)
runBreakT m = runFreeT m >>= \r -> case r of
    Pure a -> return (Just a)
    Free Zero -> return Nothing

ex1 :: Int -> BreakT IO ()
ex1 n = do
    lift $ putStr "Can you break me? :"
    str <- lift getLine
    when (product (map fromEnum str) == 1383527552)
        $ lift (putStrLn "Exactly!") >> break
    
    ex1 (succ n)

main = runBreakT (ex1 0)