@hackage / monad-coroutine

Coroutine monad transformer for suspending and resuming monadic computations

Latest0.9.3

About

Metadata

  • Last updated , by MarioBlazevic
  • License LicenseRef-GPL
  • Categories Concurrency, Monads
  • Maintained by: blamario@protonmail.com

  • Lottery factor: 1

Links

Installation

Tested Compilers

  1. 9.12.2
  2. 9.12.1
  3. 9.10.3
  4. 9.10.2
  5. 9.10.1
  6. 9.8.4
  7. 9.8.3
  8. 9.8.2
  9. 9.8.1
  10. 9.6.7
  11. 9.6.6
  12. 9.6.5
  13. 9.6.4
  14. 9.6.3
  15. 9.6.2
  16. 9.6.1
  17. 9.4.8
  18. 9.4.7
  19. 9.4.6
  20. 9.4.5
  21. 9.4.4
  22. 9.4.3
  23. 9.4.2
  24. 9.4.1
  25. 9.2.8
  26. 9.2.7
  27. 9.2.6
  28. 9.2.5
  29. 9.2.4
  30. 9.2.3
  31. 9.2.2
  32. 9.2.1
  33. 9.0.2
  34. 9.0.1
  35. 8.10.7
  36. 8.10.6
  37. 8.10.5
  38. 8.10.4
  39. 8.10.3
  40. 8.10.2
  41. 8.10.1
  42. 8.8.4
  43. 8.8.3
  44. 8.8.2
  45. 8.8.1
  46. 8.6.5
  47. 8.6.4
  48. 8.6.3
  49. 8.6.2
  50. 8.6.1
  51. 8.4.4
  52. 8.4.3
  53. 8.4.2
  54. 8.4.1
  55. 8.2.2
  56. 8.0.2
  57. 7.10.3

Readme

The monad-coroutine library, implemented by the Control.Monad.Coroutine module, provides a limited coroutine functionality in Haskell. The centerpiece of the approach is the monad transformer Coroutine, which transforms an arbitrary monadic computation into a suspendable and resumable one. The basic definition is simple:

newtype Coroutine s m r = Coroutine {resume :: m (Either (s (Coroutine s m r)) r)}

instance (Functor s, Monad m) => Monad (Coroutine s m) where
  return = Coroutine . return . Right
  t >>= f = Coroutine (resume t >>= either (return . Left . fmap (>>= f)) (resume . f))

Suspension Functors

The Coroutine transformer type is parameterized by a functor. The functor in question wraps the resumption of a suspended coroutine, and it can carry other information as well. Module Control.Monad.Coroutine.SuspensionFunctors exports some useful functors, one of which is Yield:

data Yield x y = Yield x y
instance Functor (Yield x) where
  fmap f (Yield x y) = Yield x (f y)

A coroutine parameterized by this functor is a generator which yields a value every time it suspends. For example, the following function generates the program's command-line arguments:

genArgs :: Coroutine (Yield String) IO ()
genArgs = getArgs >>= mapM_ yield

The Await functor is dual to Yield; a coroutine that suspends using this functor is a consumer coroutine that on every suspension expects to be given a value before it resumes. The following example is a consumer coroutine that prints every received value to standard output:

printer :: Show x => Coroutine (Await x) IO ()
printer = await >>= print >> printer           

While these two are the most obvious suspension functors, any functor whatsoever can be used as a coroutine suspension functor.

Running a coroutine

After a coroutine suspends, the suspension functor must be unpacked to get to the coroutine resumption. Here's an example of how the printer example could be run:

printerFeeder :: Show x => [x] -> Coroutine (Await x) IO () -> IO ()
printerFeeder [] _ = return ()
printerFeeder (head:tail) printer = do p <- resume printer
                                       case p of Left (Await p') -> printerFeeder tail (p' head)
                                                 Right result -> return result

Alternatively, you can use the function pogoStick or foldRun to the same effect:

printerFeeder feed printer = liftM snd $ foldRun f feed printer
  where f (head:tail) (Await p) = (tail, p head)
        f []          _         = ([], return ())