diff --git a/Control/Cofunctor/Ticker.hs b/Control/Cofunctor/Ticker.hs
deleted file mode 100644
--- a/Control/Cofunctor/Ticker.hs
+++ /dev/null
@@ -1,85 +0,0 @@
-{- 
-    Copyright 2010 Mario Blazevic
-
-    This file is part of the Streaming Component Combinators (SCC) project.
-
-    The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
-    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
-    version.
-
-    SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along with SCC.  If not, see
-    <http://www.gnu.org/licenses/>.
--}
-
--- | This module defines the Ticker cofunctor, useful for 'ticking off' a prefix of the input.
--- 
-
-module Control.Cofunctor.Ticker
-   (
-    -- * The Ticker type
-    Ticker(Ticker), 
-    -- * Using a Ticker
-    cofmap, splitTicked, 
-    -- * Various Ticker constructors
-    tickNone, tickOne, tickCount, tickPrefixOf, tickWhilePrefixOf, tickWhile, tickUntil, tickAll, andThen
-   )
-where
-
--- | This is a cofunctor data type for selecting a prefix of an input stream. If the next input item is acceptable, the
--- ticker function returns the ticker for the rest of the stream. If not, it returns 'Nothing'.
-newtype Ticker x = Ticker (x -> Maybe (Ticker x))
-
--- | 'Ticker' happens to be a cofunctor, but there is no standard class declaration to declare it an instance of.
-cofmap :: (x -> y) -> Ticker y -> Ticker x
-cofmap f (Ticker g) = Ticker (fmap (cofmap f) . g . f)
-
--- | Extracts a list prefix accepted by the 'Ticker' argument. Returns the modified ticker, the prefix, and the
--- remainder of the list.
-splitTicked :: Ticker x -> [x] -> (Ticker x, [x], [x])
-splitTicked t [] = (t, [], [])
-splitTicked t@(Ticker f) l@(x:rest) =
-   maybe (t, [], l) (\t' -> let (t'', xs1, xs2) = splitTicked t' rest in (t'', x:xs1, xs2)) (f x)
-
--- | A ticker that accepts no input.
-tickNone :: Ticker x
-tickNone = Ticker (const Nothing)
-
--- | A ticker that accepts a single input item.
-tickOne :: Ticker x
-tickOne = Ticker (const $ Just tickNone)
-
--- | A ticker that accepts a given number of input items.
-tickCount :: Int -> Ticker x
-tickCount n | n > 0 = Ticker (const $ Just $ tickCount (pred n))
-            | otherwise = tickNone
-
--- | A ticker that accepts the longest prefix of input that matches a prefix of the argument list.
-tickPrefixOf :: Eq x => [x] -> Ticker x
-tickPrefixOf list = tickWhilePrefixOf (map (==) list)
-
--- | A ticker that accepts a prefix of input as long as each item satisfies the predicate at the same position in the
--- argument list. The length of the predicate list thus determines the maximum number of acepted values.
-tickWhilePrefixOf :: [x -> Bool] -> Ticker x
-tickWhilePrefixOf (p : rest) = Ticker $ \x-> if p x then Just (tickWhilePrefixOf rest) else Nothing
-tickWhilePrefixOf [] = tickNone
-
--- | A ticker that accepts all input as long as it matches the given predicate.
-tickWhile :: (x -> Bool) -> Ticker x
-tickWhile p = t
-   where t = Ticker (\x-> if p x then Just t else Nothing)
-
--- | A ticker that accepts all input items until one matches the given predicate.
-tickUntil :: (x -> Bool) -> Ticker x
-tickUntil p = t
-   where t = Ticker (\x-> if p x then Nothing else Just t)
-
--- | A ticker that accepts all input.
-tickAll :: Ticker x
-tickAll = Ticker (const $ Just tickAll)
-
--- | Sequential ticker combinator: when the first argument ticker stops ticking, the second takes over.
-andThen :: Ticker x -> Ticker x -> Ticker x
-Ticker t1 `andThen` t@(Ticker t2) = Ticker (\x-> maybe (t2 x) (Just . (`andThen` t)) (t1 x))
diff --git a/Control/Monad/Coroutine.hs b/Control/Monad/Coroutine.hs
--- a/Control/Monad/Coroutine.hs
+++ b/Control/Monad/Coroutine.hs
@@ -59,7 +59,7 @@
     -- * Coroutine definition
     Coroutine(Coroutine, resume), CoroutineStepResult, suspend,
     -- * Coroutine operations
-    mapMonad, mapSuspension, 
+    mapMonad, mapSuspension, mapFirstSuspension,
     -- * Running Coroutine computations
     Naught, runCoroutine, bounce, pogoStick, foldRun, seesaw, SeesawResolver(..), seesawSteps,
     -- * Coupled Coroutine computations
@@ -68,13 +68,14 @@
    )
 where
 
-import Control.Monad (liftM)
+import Control.Applicative (Applicative(..), (<$>), liftA2)
+import Control.Monad (Monad(..), ap, liftM)
 import Control.Monad.IO.Class (MonadIO(..))
 import Control.Monad.Trans.Class (MonadTrans(..))
 import Data.Either (partitionEithers)
 import Data.Functor.Compose (Compose(..))
 
-import Control.Monad.Parallel
+import Control.Monad.Parallel (MonadParallel(..))
 
 -- | Suspending, resumable monadic computations.
 newtype Coroutine s m r = Coroutine {
@@ -85,6 +86,15 @@
 
 type CoroutineStepResult s m r = Either (s (Coroutine s m r)) r
 
+instance (Functor s, Functor m) => Functor (Coroutine s m) where
+   fmap f t = Coroutine (fmap (apply f) (resume t))
+      where apply fc (Right x) = Right (fc x)
+            apply fc (Left s) = Left (fmap (fmap fc) s)
+
+instance (Functor s, Functor m, Monad m) => Applicative (Coroutine s m) where
+   pure = return
+   (<*>) = ap
+
 instance (Functor s, Monad m) => Monad (Coroutine s m) where
    return x = Coroutine (return (Right x))
    t >>= f = Coroutine (resume t >>= apply f)
@@ -132,10 +142,17 @@
          map' (Left s) = Left (fmap (mapMonad f) s)
 
 -- | Change the suspension functor of a 'Coroutine'.
-mapSuspension :: forall s s' m x. (Functor s, Monad m) => (forall y. s y -> s' y) -> Coroutine s m x -> Coroutine s' m x
+mapSuspension :: (Functor s, Monad m) => (forall y. s y -> s' y) -> Coroutine s m x -> Coroutine s' m x
 mapSuspension f cort = Coroutine {resume= liftM map' (resume cort)}
    where map' (Right r) = Right r
          map' (Left s) = Left (f $ fmap (mapSuspension f) s)
+
+-- | Modify the first upcoming suspension of a 'Coroutine'.
+mapFirstSuspension :: forall s s' m x. (Functor s, Monad m) => 
+                      (forall y. s y -> s y) -> Coroutine s m x -> Coroutine s m x
+mapFirstSuspension f cort = Coroutine {resume= liftM map' (resume cort)}
+   where map' (Right r) = Right r
+         map' (Left s) = Left (f s)
 
 -- | Convert a non-suspending 'Coroutine' to the base monad.
 runCoroutine :: Monad m => Coroutine Naught m x -> m x
diff --git a/Control/Monad/Coroutine/SuspensionFunctors.hs b/Control/Monad/Coroutine/SuspensionFunctors.hs
--- a/Control/Monad/Coroutine/SuspensionFunctors.hs
+++ b/Control/Monad/Coroutine/SuspensionFunctors.hs
@@ -1,5 +1,5 @@
 {- 
-    Copyright 2010 Mario Blazevic
+    Copyright 2010-2011 Mario Blazevic
 
     This file is part of the Streaming Component Combinators (SCC) project.
 
@@ -17,28 +17,33 @@
 -- | This module defines some common suspension functors for use with the "Control.Monad.Coroutine" module.
 -- 
 
-{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE Rank2Types, ExistentialQuantification #-}
 
 module Control.Monad.Coroutine.SuspensionFunctors
    (
     -- * Suspension functors
-    Yield(Yield), Await(Await), Request(Request), EitherFunctor(LeftF, RightF),
-    yield, await, request,
+    Yield(Yield), Await(Await), Request(Request), ParseRequest, EitherFunctor(LeftF, RightF), eitherFunctor,
+    yield, await, request, requestParse,
     -- * Utility functions
     concatYields, concatAwaits,
     -- * Resolvers for running pairs of coroutines
     awaitYieldResolver, awaitMaybeYieldResolver, awaitYieldChunkResolver, requestsResolver, 
-    tickerYieldResolver, tickerRequestResolver, lazyTickerRequestResolver,
+    tickerYieldResolver, tickerRequestResolver, lazyTickerRequestResolver, 
+    parserRequestResolver, lazyParserRequestResolver,
     liftedTickerYieldResolver, liftedTickerRequestResolver, liftedLazyTickerRequestResolver,
+    liftedParserRequestResolver, nestedLazyParserRequestResolver,
    )
 where
 
 import Prelude hiding (foldl, foldr)
 import Control.Monad.Trans.Class (MonadTrans(..))
 import Data.Foldable (Foldable, foldl, foldr)
+import Data.Monoid (Monoid, mempty)
+import Data.Monoid.Null (MonoidNull, mnull)
+import Text.ParserCombinators.Incremental (Parser, feed, feedEof, results, (><))
 
 import Control.Monad.Coroutine
-import Control.Cofunctor.Ticker (Ticker, splitTicked)
+import Data.Functor.Contravariant.Ticker (Ticker, splitTicked)
 
 -- | The 'Yield' functor instance is equivalent to (,) but more descriptive.
 data Yield x y = Yield x y
@@ -55,12 +60,22 @@
 instance Functor (Request x f) where
    fmap f (Request x g) = Request x (f . g)
 
+data ParseRequest x z = forall y. MonoidNull y => 
+                        ParseRequest ([x] -> [x]) (Parser [x] y) ((y, Maybe (Parser [x] y)) -> z)
+instance Functor (ParseRequest x) where
+   fmap f (ParseRequest b p g) = ParseRequest b p (f . g)
+
 -- | Combines two alternative functors into one, applying one or the other. Used for nested coroutines.
 data EitherFunctor l r x = LeftF (l x) | RightF (r x)
 instance (Functor l, Functor r) => Functor (EitherFunctor l r) where
    fmap f (LeftF l) = LeftF (fmap f l)
    fmap f (RightF r) = RightF (fmap f r)
 
+-- | Like 'either' for the EitherFunctor data type.
+eitherFunctor :: (l x -> y) -> (r x -> y) -> EitherFunctor l r x -> y
+eitherFunctor left _ (LeftF f) = left f
+eitherFunctor _ right (RightF f) = right f
+
 -- | Suspend the current coroutine yielding a value.
 yield :: Monad m => x -> Coroutine (Yield x) m ()
 yield x = suspend (Yield x (return ()))
@@ -73,6 +88,10 @@
 request :: Monad m => x -> Coroutine (Request x y) m y
 request x = suspend (Request x return)
 
+-- | Suspend yielding a request and awaiting the response.
+requestParse :: (Monad m, MonoidNull y) => Parser [x] y -> Coroutine (ParseRequest x) m (y, Maybe (Parser [x] y))
+requestParse p = suspend (ParseRequest id p return)
+
 -- | Converts a coroutine yielding collections of values into one yielding single values.
 concatYields :: (Monad m, Foldable f) => Coroutine (Yield (f x)) m r -> Coroutine (Yield x) m r
 concatYields c = Coroutine{resume= resume c >>= foldChunk}
@@ -85,6 +104,8 @@
 concatAwaits c = lift (resume c) >>= either concatenate return
    where concatenate s = do chunk <- await
                             concatAwaits (feedAll chunk (suspend s))
+         feedAll :: (Foldable f, Monad m) => f x -> Coroutine (Await x) m r -> Coroutine (Await x) m r
+         feedAll chunk c = foldl (flip feedCoroutine) c chunk
 
 -- | A 'SeesawResolver' for running two coroutines in parallel, one of which 'await's values while the other 'yield's
 -- them. The yielding coroutine must not terminate before the other one.
@@ -190,10 +211,82 @@
                      next:_ -> cont (c1 (chunk, Left next)) (suspend $ lifter $ Request rest c2)
 }
 
+-- | Like 'parserYieldResolver', the only difference being that the producing coroutine sends its chunks using 'request'
+-- rather than 'yield'. The feedback received from 'request' is the unconsumed remainder of the chunk, which lets the
+-- coroutine know when its sibling terminates.
+parserRequestResolver :: Monoid y => SeesawResolver (Request (Parser [x] y) y) (Request [x] [x])
+                                                    (Request (Parser [x] y) y) (Request [x] [x])
+parserRequestResolver = liftedParserRequestResolver id id
+
+-- | A generic version of 'parserRequestResolver', allowing coroutines with 'Request' functors embedded in other
+-- functors.
+liftedParserRequestResolver :: (Functor s1, Functor s2, Monoid y) =>
+                               (forall a. Request (Parser [x] y) y a -> s1 a) -> (forall a. Request [x] [x] a -> s2 a)
+                               -> SeesawResolver (Request (Parser [x] y) y) (Request [x] [x]) s1 s2
+liftedParserRequestResolver lift1 lift2 = SeesawResolver {
+   resumeLeft= \(Request _ c)-> c mempty,
+   resumeRight= \(Request chunk c)-> c chunk,
+   resumeBoth= \cont (Request p c1) (Request xs c2)->
+               case results (feed xs p)
+               of ([], Just (r, p')) -> cont (suspend $ lift1 $ Request (return r >< p') c1) (c2 [])
+                  ([(r, [])], Nothing) -> cont (c1 r) (c2 [])
+                  ([(r, rest)], Nothing) -> cont (c1 r) (suspend $ lift2 $ Request rest c2)
+                  _ -> error "Multiple results!"
+}
+
+-- | Like 'parserRequestResolver', except the consuming coroutine requests receive both the selected prefix of the input
+-- chunk and a peek at either the next unconsumed input item, if any, or the final 'Parser' value. Chunks sent by the
+-- producing coroutine never get combined for the consuming coroutine. This allows better synchronization between the
+-- two coroutines. It also leaks the information about the produced chunk boundaries into the consuming coroutine, so
+-- this resolver should be used with caution.
+lazyParserRequestResolver :: SeesawResolver (ParseRequest x) (Request [x] [x]) (ParseRequest x) (Request [x] [x])
+lazyParserRequestResolver = SeesawResolver {
+   resumeLeft= \(ParseRequest b p c)-> mapFirstSuspension (prependToParseRequest b) $
+                                       case results (feedEof p)
+                                       of ([], Nothing) -> c (mempty, Nothing)
+                                          ([(r, _)], Nothing) -> c (r, Nothing)
+                                          _ -> error "Multiple results!",
+   resumeRight= \(Request chunk c)-> c chunk,
+   resumeBoth= \cont (ParseRequest b p c1) (Request xs c2)->
+               case results (if null xs then feedEof p else feed xs p)
+                    of ([], Nothing) -> 
+                          cont (c1 (mempty, Nothing)) (if null xs then c2 $ b [] else suspend $ Request (b xs) c2)
+                       ([], Just (r, p')) -> 
+                          cont (if mnull r then suspend $ ParseRequest (b . (xs ++)) p' c1 else c1 (r, Just p')) (c2 [])
+                       ([(r, [])], Nothing) -> cont (c1 (r, Nothing)) (c2 [])
+                       ([(r, rest)], Nothing) -> cont (c1 (r, Nothing)) (suspend $ Request rest c2)
+                       (_, Nothing) -> error "Multiple results!"
+}
+
+-- | A generic version of 'lazyParserRequestResolver', allowing coroutines with 'Request' functors embedded in other
+-- functors.
+nestedLazyParserRequestResolver ::
+   (Functor s1, Functor s2) => 
+   SeesawResolver (ParseRequest x) (Request [x] [x])
+                  (EitherFunctor s1 (ParseRequest x)) (EitherFunctor s2 (Request [x] [x]))
+nestedLazyParserRequestResolver = SeesawResolver {
+   resumeLeft= \(ParseRequest b p c)-> case results (feedEof p)
+                                       of ([], Nothing) -> mapFirstSuspension (retry b) $ c (mempty, Nothing)
+                                          ([(r, t)], Nothing) -> mapFirstSuspension (retry (t ++)) $ c (r, Nothing)
+                                          (_, Nothing) -> error "Multiple results!",
+   resumeRight= \(Request chunk c)-> c chunk,
+   resumeBoth= \cont (ParseRequest b p c1) (Request xs c2)->
+               case results (if null xs then feedEof p else feed xs p)
+               of ([], Nothing) ->
+                     cont (c1 (mempty, Nothing)) (if null xs then c2 (b []) else suspend $ RightF $ Request (b xs) c2)
+                  ([], Just (r, p')) ->
+                     cont
+                        (if mnull r then suspend $ RightF $ ParseRequest (b . (xs ++)) p' c1 else c1 (r, Just p'))
+                        (c2 [])
+                  ([(r, rest)], Nothing) ->
+                     cont (c1 (r, Nothing)) (if null rest then c2 [] else suspend $ RightF $ Request rest c2)
+                  _ -> error "Multiple results!"
+}
+   where retry prefix = eitherFunctor LeftF (RightF . feedList prefix)
+         feedList b (ParseRequest b' p c) = ParseRequest (b' . b) (feed (b []) p) c
+
 -- | Feeds a single value to an awaiting coroutine.
-feed :: Monad m => x -> Coroutine (Await x) m r -> Coroutine (Await x) m r
-feed x c = bounce (\(Await f)-> f x) c
+feedCoroutine :: Monad m => x -> Coroutine (Await x) m r -> Coroutine (Await x) m r
+feedCoroutine x c = bounce (\(Await f)-> f x) c
 
--- | Feeds a collection of values to an awaiting coroutine.
-feedAll :: (Foldable f, Monad m) => f x -> Coroutine (Await x) m r -> Coroutine (Await x) m r
-feedAll chunk c = foldl (flip feed) c chunk
+prependToParseRequest b (ParseRequest b' p' c') = ParseRequest (b . b') p' c'
diff --git a/Data/Functor/Contravariant/Ticker.hs b/Data/Functor/Contravariant/Ticker.hs
new file mode 100644
--- /dev/null
+++ b/Data/Functor/Contravariant/Ticker.hs
@@ -0,0 +1,102 @@
+{- 
+    Copyright 2010 Mario Blazevic
+
+    This file is part of the Streaming Component Combinators (SCC) project.
+
+    The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+    version.
+
+    SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along with SCC.  If not, see
+    <http://www.gnu.org/licenses/>.
+-}
+
+-- | This module defines the Ticker cofunctor, useful for 'ticking off' a prefix of the input.
+-- 
+
+module Data.Functor.Contravariant.Ticker
+   (
+    -- * The Ticker type
+    Ticker(Ticker), 
+    -- * Using a Ticker
+    splitTicked, Contravariant(..),
+    -- * Ticker constructors
+    tickNone, tickOne, tickCount, tickPrefixOf, tickWhilePrefixOf, tickWhile, tickUntil, tickAll, 
+    -- * Ticker combinators
+    andThen, and, or
+   )
+where
+
+import Prelude hiding (and, or)
+import Control.Monad (liftM2)
+import Data.Functor.Contravariant (Contravariant(contramap))
+
+-- | This is a contra-functor data type for selecting a prefix of an input stream. If the next input item is acceptable,
+-- the ticker function returns the ticker for the rest of the stream. If not, it returns 'Nothing'.
+newtype Ticker x = Ticker (x -> Maybe (Ticker x))
+
+instance Contravariant Ticker where
+   contramap f (Ticker g) = Ticker (fmap (contramap f) . g . f)
+
+-- | Extracts a list prefix accepted by the 'Ticker' argument. Returns the modified ticker, the prefix, and the
+-- remainder of the list.
+splitTicked :: Ticker x -> [x] -> (Ticker x, [x], [x])
+splitTicked t [] = (t, [], [])
+splitTicked t@(Ticker f) l@(x:rest) =
+   maybe (t, [], l) (\t' -> let (t'', xs1, xs2) = splitTicked t' rest in (t'', x:xs1, xs2)) (f x)
+
+-- | A ticker that accepts no input.
+tickNone :: Ticker x
+tickNone = Ticker (const Nothing)
+
+-- | A ticker that accepts a single input item.
+tickOne :: Ticker x
+tickOne = Ticker (const $ Just tickNone)
+
+-- | A ticker that accepts a given number of input items.
+tickCount :: Int -> Ticker x
+tickCount n | n > 0 = Ticker (const $ Just $ tickCount (pred n))
+            | otherwise = tickNone
+
+-- | A ticker that accepts the longest prefix of input that matches a prefix of the argument list.
+tickPrefixOf :: Eq x => [x] -> Ticker x
+tickPrefixOf list = tickWhilePrefixOf (map (==) list)
+
+-- | A ticker that accepts a prefix of input as long as each item satisfies the predicate at the same position in the
+-- argument list. The length of the predicate list thus determines the maximum number of acepted values.
+tickWhilePrefixOf :: [x -> Bool] -> Ticker x
+tickWhilePrefixOf (p : rest) = Ticker $ \x-> if p x then Just (tickWhilePrefixOf rest) else Nothing
+tickWhilePrefixOf [] = tickNone
+
+-- | A ticker that accepts all input as long as it matches the given predicate.
+tickWhile :: (x -> Bool) -> Ticker x
+tickWhile p = t
+   where t = Ticker (\x-> if p x then Just t else Nothing)
+
+-- | A ticker that accepts all input items until one matches the given predicate.
+tickUntil :: (x -> Bool) -> Ticker x
+tickUntil p = t
+   where t = Ticker (\x-> if p x then Nothing else Just t)
+
+-- | A ticker that accepts all input.
+tickAll :: Ticker x
+tickAll = Ticker (const $ Just tickAll)
+
+-- | Sequential concatenation ticker combinator: when the first argument ticker stops ticking, the second takes over.
+andThen :: Ticker x -> Ticker x -> Ticker x
+Ticker t1 `andThen` t@(Ticker t2) = Ticker (\x-> maybe (t2 x) (Just . (`andThen` t)) (t1 x))
+
+-- | Parallel conjunction ticker combinator: the result keeps ticking as long as both arguments do.
+and :: Ticker x -> Ticker x -> Ticker x
+Ticker t1 `and` Ticker t2 = Ticker (\x-> liftM2 and (t1 x) (t2 x))
+
+-- | Parallel choice ticker combinator: the result keeps ticking as long as any of the arguments does.
+or :: Ticker x -> Ticker x -> Ticker x
+Ticker t1 `or` Ticker t2 = Ticker (\x-> case (t1 x, t2 x)
+                                        of (Nothing, Nothing) -> Nothing
+                                           (Nothing, t'@Just{}) -> t'
+                                           (t'@Just{}, Nothing) -> t'
+                                           (Just t1', Just t2') -> Just (t1' `or` t2'))
diff --git a/Test/BenchmarkCoroutine.hs b/Test/BenchmarkCoroutine.hs
new file mode 100644
--- /dev/null
+++ b/Test/BenchmarkCoroutine.hs
@@ -0,0 +1,121 @@
+{- 
+    Copyright 2010 Mario Blazevic
+
+    This file is part of the Streaming Component Combinators (SCC) project.
+
+    The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+    version.
+
+    SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along with SCC.  If not, see
+    <http://www.gnu.org/licenses/>.
+-}
+
+-- | The "Control.Monad.Coroutine" tests.
+
+module Main where
+
+import Prelude hiding (sequence)
+import Control.Exception (assert)
+import Control.Monad (liftM, mapM, when)
+import Control.Parallel (pseq)
+import Data.Functor.Compose (Compose(..))
+import Data.Functor.Identity (runIdentity)
+import Data.List (find)
+import Data.Maybe (fromJust)
+import System.Environment (getArgs)
+
+import Control.Monad.Coroutine
+import Control.Monad.Coroutine.SuspensionFunctors
+import Control.Monad.Coroutine.Nested
+import Control.Monad.Parallel (MonadParallel, bindM2, liftM2, sequence)
+
+import Criterion.Main
+
+factors n = maybe [n] (\k-> (k : factors (n `div` k))) (find (\k-> n `mod` k == 0) [2 .. n - 1])
+
+fib x 0 | x >= 0 = 1
+fib _ 1 = 1
+fib x n = fib x (n - 2) + fib x (n - 1)
+
+factorFibs :: MonadParallel m => [Int] -> m Integer
+factorFibs nums = liftM snd $
+                  seesaw bindM2 (SeesawResolver resumeLeft resumeRight resumeBoth)
+                     (mapM_ (yieldApply (fib 0)) nums)
+                     (factorize 0)
+   where factorize :: MonadParallel m => Integer -> Coroutine (Await (Maybe Integer)) m Integer
+         factorize sum = await
+                         >>= maybe
+                                (return sum)
+                                (\n-> factorize (sum + n {-product (factors n)-}))
+         resumeLeft (Yield _ c) = c
+         resumeRight (Await c) = c Nothing
+         resumeBoth c (Yield x c1) (Await c2) = c c1 (c2 (Just x))
+
+twoFibs :: MonadParallel m => [Int] -> m Integer
+twoFibs nums = pogoStick resume (couple bindM2 (fibs 1) (fibs 2))
+               >>= \(x, y)-> return (x + y)
+   where resume :: SomeFunctor (Yield Integer) (Yield Integer) c -> c
+         resume (Both (Compose (Yield n1 (Yield n2 c)))) = assert (n1 == n2) c
+         fibs ix = mapM_ (yieldApply (fib ix)) nums >> applyM (fib ix) (last nums)
+
+twoFibsSeesaw :: MonadParallel m => [Int] -> m Integer
+twoFibsSeesaw nums = liftM (uncurry (+)) $
+                     seesaw bindM2 resolver (fibs 1) (fibs 2)
+   where resolver = SeesawResolver{
+                      resumeLeft= undefined,
+                      resumeRight= undefined,
+                      resumeBoth= \cont (Yield left c1) (Yield right c2)-> assert (left == right) $ cont c1 c2
+                    }
+         fibs ix = mapM_ (yieldApply (fib ix)) nums >> applyM (fib ix) (last nums)
+
+fibs :: MonadParallel m => Int -> [Int] -> m Integer
+fibs coroutineCount nums = liftM sum $
+                           pogoStick
+                              resume
+                              (merge sequence appendYields $ replicateIx coroutineCount fibs)
+   where resume :: Yield [Integer] (Coroutine (Yield [Integer]) m [Integer]) -> Coroutine (Yield [Integer]) m [Integer]
+         resume (Yield (x:xs) c) = assert (all (==x) xs) c
+         fibs ix = mapM_ (yieldApply ((:[]) . fib ix)) nums >> applyM (fib ix) (last nums)
+         appendYields :: [Yield [s] x] -> Yield [s] [x]
+         appendYields yields = uncurry Yield $ foldr (\(Yield s x) (ss, xs)-> (s ++ ss, x:xs)) ([], []) yields
+
+yieldApply f n = let result = f n in result `pseq` yield result
+applyM f n = let result = f n in result `pseq` return result
+
+replicateIx :: Int -> (Int -> x) -> [x]
+replicateIx n f = map f [1..n]
+
+nested :: (Monad m, Functor p) =>
+          Int -> (Integer -> Coroutine p m ()) -> Coroutine (EitherFunctor p (Yield Integer)) m ()
+nested level suspendParent = do mapSuspension RightF (yield 1)
+                                liftAncestor (suspendParent 2)
+                                when (level > 0) (pogoStickNested cont $
+                                                  nested (pred level) (liftAncestor . suspendParent))
+   where cont (Yield x c) = c
+
+runNested size = liftM fst $ foldRun add 0 (nested size yield)
+   where add s (LeftF (Yield n c)) = (s + n, c)
+         add s (RightF (Yield n c)) = (s + 10 * n, c)
+
+
+main = defaultMain ([bgroup "Identity" [bench name (nf (runIdentity . task name) size) | (name, size) <- tasks],
+                     bgroup "Maybe" [bench name (nf (fromJust . task name) size) | (name, size) <- tasks],
+                     bgroup "List" [bench name (nf (head . task name) size) | (name, size) <- tasks],
+                     bgroup "IO" [bench name (task name size :: IO Integer) | (name, size) <- tasks]])
+
+tasks = [("fib-factor", 32), ("2fibs", 30), ("2fibsSeesaw", 30), ("nested", 250),
+         ("1*fibs", 33), ("2*fibs", 33), ("3*fibs", 33), ("4*fibs", 33)]
+
+task :: MonadParallel m => String -> Int -> m Integer
+task taskName size = 
+   case taskName 
+   of "fib-factor" -> factorFibs [1 .. size]
+      "2fibs" -> twoFibs [1 .. size]
+      "2fibsSeesaw" -> twoFibsSeesaw [1 .. size]
+      "nested" -> runNested size
+      coroutineCount : "*fibs" -> fibs (read [coroutineCount]) [1 .. size]
+      _ -> error "Bad task."
diff --git a/TestCoroutine.hs b/TestCoroutine.hs
deleted file mode 100644
--- a/TestCoroutine.hs
+++ /dev/null
@@ -1,121 +0,0 @@
-{- 
-    Copyright 2010 Mario Blazevic
-
-    This file is part of the Streaming Component Combinators (SCC) project.
-
-    The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
-    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
-    version.
-
-    SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along with SCC.  If not, see
-    <http://www.gnu.org/licenses/>.
--}
-
--- | The "Control.Monad.Coroutine" tests.
-
-module Main where
-
-import Prelude hiding (sequence)
-import Control.Exception (assert)
-import Control.Monad (liftM, mapM, when)
-import Control.Parallel (pseq)
-import Data.Functor.Compose (Compose(..))
-import Data.Functor.Identity (runIdentity)
-import Data.List (find)
-import Data.Maybe (fromJust)
-import System.Environment (getArgs)
-
-import Control.Monad.Coroutine
-import Control.Monad.Coroutine.SuspensionFunctors
-import Control.Monad.Coroutine.Nested
-import Control.Monad.Parallel (MonadParallel, bindM2, liftM2, sequence)
-
-factors n = maybe [n] (\k-> (k : factors (n `div` k))) (find (\k-> n `mod` k == 0) [2 .. n - 1])
-
-fib x 0 | x >= 0 = 1
-fib _ 1 = 1
-fib x n = fib x (n - 2) + fib x (n - 1)
-
-factorFibs :: MonadParallel m => [Int] -> m Integer
-factorFibs nums = liftM snd $
-                  seesaw bindM2 (SeesawResolver resumeLeft resumeRight resumeBoth)
-                     (mapM_ (yieldApply (fib 0)) nums)
-                     (factorize 0)
-   where factorize :: MonadParallel m => Integer -> Coroutine (Await (Maybe Integer)) m Integer
-         factorize sum = await
-                         >>= maybe
-                                (return sum)
-                                (\n-> factorize (sum + n {-product (factors n)-}))
-         resumeLeft (Yield _ c) = c
-         resumeRight (Await c) = c Nothing
-         resumeBoth c (Yield x c1) (Await c2) = c c1 (c2 (Just x))
-
-twoFibs :: MonadParallel m => [Int] -> m Integer
-twoFibs nums = pogoStick resume (couple bindM2 (fibs 1) (fibs 2))
-               >>= \(x, y)-> return (x + y)
-   where resume :: SomeFunctor (Yield Integer) (Yield Integer) c -> c
-         resume (Both (Compose (Yield n1 (Yield n2 c)))) = assert (n1 == n2) c
-         fibs ix = mapM_ (yieldApply (fib ix)) nums >> applyM (fib ix) (last nums)
-
-twoFibsSeesaw :: MonadParallel m => [Int] -> m Integer
-twoFibsSeesaw nums = liftM (uncurry (+)) $
-                     seesaw bindM2 resolver (fibs 1) (fibs 2)
-   where resolver = SeesawResolver{
-                      resumeLeft= undefined,
-                      resumeRight= undefined,
-                      resumeBoth= \cont (Yield left c1) (Yield right c2)-> assert (left == right) $ cont c1 c2
-                    }
-         fibs ix = mapM_ (yieldApply (fib ix)) nums >> applyM (fib ix) (last nums)
-
-fibs :: MonadParallel m => Int -> [Int] -> m Integer
-fibs coroutineCount nums = liftM sum $
-                           pogoStick
-                              resume
-                              (merge sequence appendYields $ replicateIx coroutineCount fibs)
-   where resume :: Yield [Integer] (Coroutine (Yield [Integer]) m [Integer]) -> Coroutine (Yield [Integer]) m [Integer]
-         resume (Yield (x:xs) c) = assert (all (==x) xs) c
-         fibs ix = mapM_ (yieldApply ((:[]) . fib ix)) nums >> applyM (fib ix) (last nums)
-         appendYields :: [Yield [s] x] -> Yield [s] [x]
-         appendYields yields = uncurry Yield $ foldr (\(Yield s x) (ss, xs)-> (s ++ ss, x:xs)) ([], []) yields
-
-yieldApply f n = let result = f n in result `pseq` yield result
-applyM f n = let result = f n in result `pseq` return result
-
-replicateIx :: Int -> (Int -> x) -> [x]
-replicateIx n f = map f [1..n]
-
-nested :: (Monad m, Functor p) =>
-          Int -> (Integer -> Coroutine p m ()) -> Coroutine (EitherFunctor p (Yield Integer)) m ()
-nested level suspendParent = do mapSuspension RightF (yield 1)
-                                liftAncestor (suspendParent 2)
-                                when (level > 0) (pogoStickNested cont $ nested (pred level) (liftAncestor . suspendParent))
-   where cont (Yield x c) = c
-
-main = do args <- getArgs
-          if length args /= 4
-             then putStr help
-             else do let [taskName, monad, size, coroutineCount] = args
-                         task :: MonadParallel m => m Integer
-                         task = case taskName of "fib-factor" -> factorFibs [1 .. read size]
-                                                 "2fibs" -> twoFibs [1 .. read size]
-                                                 "2fibsSeesaw" -> twoFibsSeesaw [1 .. read size]
-                                                 "fibs" -> fibs (read coroutineCount) [1 .. read size]
-                                                 "nested" -> liftM fst $ foldRun add 0 (nested (read size) yield)
-                                                    where add s (LeftF (Yield n c)) = (s + n, c)
-                                                          add s (RightF (Yield n c)) = (s + 10 * n, c)
-                                                 _ -> error (help ++ "Bad task.")
-                     result <- case monad of "Maybe" -> return $ fromJust task
-                                             "[]" -> return $ head task
-                                             "Identity" -> return $ runIdentity task
-                                             "IO" -> task
-                                             _ -> error (help ++ "Bad monad.")
-                     print result
-
-help = "Usage: test-coroutine <task> <monad> <size> <coroutines>?\n"
-       ++ "  where <task>       is 'fib-factor' or 'fibs',\n"
-       ++ "        <monad>      is 'Identity', 'Maybe', '[]', or 'IO',\n"
-       ++ "        <size>       is the size of the task,\n"
-       ++ "    and <coroutines> is the number of coroutines to employ.\n"
diff --git a/monad-coroutine.cabal b/monad-coroutine.cabal
--- a/monad-coroutine.cabal
+++ b/monad-coroutine.cabal
@@ -1,5 +1,5 @@
 Name:                monad-coroutine
-Version:             0.6.1
+Version:             0.7
 Cabal-Version:       >= 1.2
 Build-Type:          Simple
 Synopsis:            Coroutine monad transformer for suspending and resuming monadic computations
@@ -12,17 +12,18 @@
   
 License:             GPL
 License-file:        LICENSE.txt
-Copyright:           (c) 2010 Mario Blazevic
+Copyright:           (c) 2010-2011 Mario Blazevic
 Author:              Mario Blazevic
 Maintainer:          blamario@yahoo.com
 Homepage:            http://trac.haskell.org/SCC/wiki/monad-coroutine
-Extra-source-files:  TestCoroutine.hs
+Extra-source-files:  Test/BenchmarkCoroutine.hs
 -- Source-repository head
 --   type:              darcs
 --   location:          http://code.haskell.org/SCC/
 
 Library
-  Exposed-Modules:   Control.Cofunctor.Ticker,
+  Exposed-Modules:   Data.Functor.Contravariant.Ticker,
                      Control.Monad.Coroutine, Control.Monad.Coroutine.SuspensionFunctors, Control.Monad.Coroutine.Nested
-  Build-Depends:     base < 5, monad-parallel, transformers >= 0.2 && < 0.3
+  Build-Depends:     base < 5, transformers >= 0.2 && < 0.3, contravariant >= 0.1 && < 0.2,
+                     monad-parallel, incremental-parser < 1.0
   GHC-prof-options:  -auto-all
