This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

PauseT monad transformer, using operational monad

By: a guest on Feb 23rd, 2013  |  syntax: Haskell  |  size: 3.59 KB  |  views: 113  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. {-# LANGUAGE GADTs, RankNTypes #-}
  2.  
  3. -- | Example of using the operational monad to pause computations of
  4. -- any other monad.
  5. module PauseT where
  6.  
  7. import Control.Monad ((<=<), liftM)
  8. import Control.Monad.Trans (lift)
  9. import Control.Monad.Operational
  10. import Control.Concurrent (threadDelay)
  11.  
  12. -- | 'PauseT' is the language of monadic programs that can pause
  13. -- themselves--return prematurely to their callers, who have the
  14. -- option of resuming them where they paused.
  15. --
  16. -- 'PauseT' is a monad transformer, which means that we can \"stack\"
  17. -- it on top of any monad.  So what it does is it adds the pause
  18. -- feature to a smaller language (the @m@ type variable).
  19. type PauseT m a = ProgramT PauseInstruction m a
  20.  
  21. -- | The 'PauseT' language has only one primitive instruction, that
  22. -- produces no useful intermediate value.
  23. --
  24. -- If just one instruction sounds useless, remember that 'PauseT' is a
  25. -- monad transformer, so in practice this will work as an *add-on* to
  26. -- another language.
  27. data PauseInstruction a where
  28.     Pause :: PauseInstruction ()
  29.  
  30. -- | Convenience wrapper around 'Pause' to encapsulate the fact that
  31. -- it's a primitive instruction.
  32. pause = singleton Pause
  33.  
  34. -- | This function is the interepreter for 'PauseT' programs.
  35. runPauseT :: Monad m => PauseT m a -> m (Either (PauseT m a) a)  
  36. runPauseT = eval <=< viewT
  37.     where
  38.       -- Intepreting a program breaks into two subcases.  The first
  39.       -- one is the 'Return' case, where we specify how to handle a
  40.       -- program that has completed.
  41.       eval (Return a) = return (Right a)
  42.       -- The second subcase is @instruction :>>= continuation@; here
  43.       -- we specify how to interpret each instruction of the language.
  44.       -- The continuation is represented as a function that takes a
  45.       -- result of the same type as the instruction's result type, and
  46.       -- returns the rest of the program.  In this case, the only
  47.       -- primitive instruction is 'Pause', and what we do is package
  48.       -- up the continuation into a 'Left' constructor for 'Either'.
  49.       eval (Pause :>>= k) = return (Left (k ()))
  50.  
  51. -- | A driver function to step through a 'PauseT' program.  The first
  52. -- argument, the stepper, is an action that can execute actions of its
  53. -- own choice, but must at some point execute the rest of the 'PauseT'
  54. -- program.
  55. step :: Monad m => (forall a. m a -> m a) -> PauseT m a -> m a
  56. step stepper pausable =
  57.     do result <- runPauseT pausable
  58.        case result of
  59.          Left continuation -> stepper (step stepper continuation)
  60.          Right result -> return result
  61.  
  62. -- | We're going to use this helper function with 'step' in the examples.
  63. pressReturn :: IO ()
  64. pressReturn = do putStr "Press <return> to continue..."
  65.                  getLine
  66.                  return ()
  67.  
  68. -- | Example PauseT program.
  69. example :: PauseT IO ()
  70. example = do lift $ putStrLn "Goodbye"
  71.              pause
  72.              lift $ putStrLn "Cruel"
  73.              pause
  74.              lift $ putStrLn "World!"
  75.  
  76. -- Now, using 'step' and different steppers.  The first one just
  77. -- ignores the pauses:
  78. --
  79. -- @
  80. -- *Main> step id example
  81. -- Goodbye
  82. -- Cruel
  83. -- World!
  84. -- @
  85. --
  86. -- The second one prompts us to press return at each pause, then
  87. -- continues the program:
  88. --
  89. -- @
  90. -- *Main> step (pressReturn>>) example
  91. -- Goodbye
  92. -- Press <return> to continue...
  93. -- Cruel
  94. -- Press <return> to continue...
  95. -- World!
  96. -- @
  97. --
  98. -- The third and final one pauses 1.5 seconds at each 'pause':
  99. --
  100. -- @
  101. -- *Main> step (threadDelay 1500000>>) example
  102. -- Goodbye
  103. --   (pause 1.5s)
  104. -- Cruel
  105. --   (pause 1.5s)
  106. -- World!
  107. -- @
clone this paste RAW Paste Data