monad-coroutine: Coroutine monad transformer for suspending and resuming monadic computations

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

This package defines a monad transformer, applicable to any monad, that allows the monadic computation to suspend and to be later resumed. The transformer is parameterized by an arbitrary functor, used to store the suspended computation's resumption.

[Skip to Readme]


Versions 0.5, 0.5.1, 0.6, 0.6.1, 0.7, 0.7.1, 0.8,, 0.9,,,,, 0.9.1,,,,, 0.9.2
Change log None available
Dependencies base (>=4.9 && <5), monad-parallel (<1.0), transformers (>=0.2 && <0.6), transformers-compat (>=0.3 && <0.7) [details]
License LicenseRef-GPL
Copyright (c) 2010-2018 Mario Blazevic
Author Mario Blazevic
Category Concurrency, Control, Monads
Home page
Source repo head: darcs get
Uploaded by MarioBlazevic at 2021-05-27T14:05:05Z


[Index] [Quick Jump]


Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Readme for monad-coroutine-

[back to package description]

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 ())