Freeモナド実用の旅(2): Gotoモナド
かなりシンプルに実装できた。よい子は絶対にマネしないでね!
import qualified Data.IntMap as M import Control.Monad.Trans.Free import Control.Monad.Trans newtype Label = LabelId Int deriving (Eq, Ord) data Labeling a = Label (Label -> a) | Goto Label -- deriving Functorがあれば不要 instance Functor Labeling where fmap f (Label g) = Label (f . g) fmap f (Goto l) = Goto l type GotoT = FreeT Labeling label :: Monad m => GotoT m Label label = liftF (Label id) goto :: Monad m => Label -> GotoT m () goto l = liftF (Goto l) runGotoT :: Monad m => GotoT m a -> m a runGotoT = run M.empty where run st m = runFreeT m >>= \r -> case r of Pure a -> return a Free (Label f) -> let cont = f (LabelId newLabel) in run (M.insert newLabel cont st) cont Free (Goto (LabelId i)) -> run st (st M.! i) where newLabel = succ (M.size st) ex2 = do lift $ putStrLn "Begin." hoge <- label lift $ putStrLn "Label hoge." piyo <- label lift $ putStrLn "Label piyo." fuga <- label lift $ putStrLn "Label fuga." lift $ putStr "Where do you want to go? " str <- lift getLine case str of "hoge" -> goto hoge "piyo" -> goto piyo "fuga" -> goto fuga _ -> lift $ putStrLn "Quit." main = runGotoT ex2