diff --git a/Text/Parcom/Combinators.hs b/Text/Parcom/Combinators.hs
--- a/Text/Parcom/Combinators.hs
+++ b/Text/Parcom/Combinators.hs
@@ -11,26 +11,26 @@
 import Text.Parcom.Internal
 
 -- | Walk a list of options, return the first one that succeeds.
-choice :: (Stream s t) => [Parcom s t a] -> Parcom s t a
+choice :: (Monad m, Stream s t) => [ParcomT s t m a] -> ParcomT s t m a
 choice xs = foldl (<|>) empty xs <?> "I tried to make a choice, but couldn't"
 
 -- | Like @choice@, but each choice tagged with a human-readable name for
 -- better error reporting.
-namedChoice :: (Stream s t) => [(String, Parcom s t a)] -> Parcom s t a
+namedChoice :: (Monad m, Stream s t) => [(String, ParcomT s t m a)] -> ParcomT s t m a
 namedChoice xs = choice (map snd xs) <?> (formatOptionList . map fst) xs
 
 -- | Match two consecutive parser, return the first parser's result iff both
 -- succeed.
-before :: (Stream s t) => Parcom s t a -> Parcom s t b -> Parcom s t a
+before :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m b -> ParcomT s t m a
 before p q = do { v <- p; q; return v }
 
 -- | Match three consecutive parsers, return the middle parser's result iff
 -- all three match. Parsers are given in the order inner, left, right.
-between :: (Stream s t) => Parcom s t a -> Parcom s t l -> Parcom s t r -> Parcom s t a
+between :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m l -> ParcomT s t m r -> ParcomT s t m a
 between p l r = do { l; v <- p; r; return v }
 
 -- | Match zero or more occurrences of a parser
-many :: (Stream s t) => Parcom s t a -> Parcom s t [a]
+many :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m [a]
 many p =
     handle p f m
     where
@@ -40,7 +40,7 @@
             return (x:xs)
 
 -- | Match one or more occurrences of a parser
-many1 :: (Stream s t) => Parcom s t a -> Parcom s t [a]
+many1 :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m [a]
 many1 p = do
     xs <- many p
     if null xs
@@ -49,7 +49,7 @@
 
 -- | Given an item parser and a separator parser, keep parsing until the
 -- separator or the item fails.
-manySepBy :: (Stream s t) => Parcom s t a -> Parcom s t b -> Parcom s t [a]
+manySepBy :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m b -> ParcomT s t m [a]
 manySepBy p s = go
     where
         go = do
@@ -66,7 +66,7 @@
                     (\_ -> go >>= \xs -> return (x:xs))
 
 -- | Run the given parser n times, returning all the results as a list.
-times :: (Stream s t) => Int -> Parcom s t a -> Parcom s t [a]
+times :: (Monad m, Stream s t) => Int -> ParcomT s t m a -> ParcomT s t m [a]
 times 0 p = return []
 times n p = do
     x <- p
@@ -74,5 +74,5 @@
     return (x:xs)
 
 -- | Ignore the result of a parser.
-skip :: Parcom s t a -> Parcom s t ()
+skip :: Monad m => ParcomT s t m a -> ParcomT s t m ()
 skip p = p >> return ()
diff --git a/Text/Parcom/Core.hs b/Text/Parcom/Core.hs
--- a/Text/Parcom/Core.hs
+++ b/Text/Parcom/Core.hs
@@ -2,8 +2,8 @@
 module Text.Parcom.Core
 ( ParcomError (..)
 , SourcePosition (..)
-, Parcom
-, parse
+, ParcomT, parseT
+, Parcom, parse
 , peek, next, atEnd
 , try, handle
 , notFollowedBy
@@ -16,6 +16,8 @@
 import Text.Parcom.Stream (Stream, Token)
 import Control.Monad (liftM, ap)
 import Control.Applicative
+import Control.Monad.Identity
+import Control.Monad.Trans.Class
 
 data SourcePosition =
     SourcePosition
@@ -38,92 +40,111 @@
         , psStream :: s
         }
 
-newtype Parcom s t a = Parcom { runParcom :: ParcomState s -> (Either ParcomError a, ParcomState s) }
+type Parcom s t a = ParcomT s t Identity a
+newtype ParcomT s t m a = ParcomT { runParcomT :: ParcomState s -> m (Either ParcomError a, ParcomState s) }
 
-instance Monad (Parcom s t) where
-    return x = Parcom (\s -> (Right x, s))
-    fail err = Parcom (\s -> (Left $ ParcomError err (psSourcePosition s), s))
-    m >>= f = Parcom $ \s ->
-        let (a, s') = runParcom m s
-        in case a of
-            Left e -> (Left e, s')
-            Right x -> runParcom (f x) s'
+instance (Monad m) => Monad (ParcomT s t m) where
+    return x = ParcomT (\s -> return (Right x, s))
+    fail err = ParcomT (\s -> return (Left $ ParcomError err (psSourcePosition s), s))
+    m >>= f = ParcomT $ \s -> do -- in the m Monad
+        (a, s') <- runParcomT m s
+        case a of
+            Left err -> return (Left err, s')
+            Right ma -> do
+                runParcomT (f ma) s'
 
-instance Functor (Parcom s t) where
+instance MonadTrans (ParcomT s t) where
+    lift a = ParcomT $ \s -> do
+        v <- a
+        return (Right v, s)
+
+instance (Monad m) => Functor (ParcomT s t m) where
     fmap f xs = xs >>= return . f
 
-instance Applicative (Parcom s t) where
+instance (Monad m) => Applicative (ParcomT s t m) where
     pure = return
     (<*>) = ap
 
-instance Alternative (Parcom s t) where
+instance (Monad m) => Alternative (ParcomT s t m) where
     (<|>) = alt
     empty = fail "empty"
 
-runParser :: (Stream s t, Token t) => Parcom s t a -> String -> s -> (Either ParcomError a, ParcomState s)
-runParser p fn str =
-    runParcom p state
+runParserT :: (Stream s t, Token t) => ParcomT s t m a -> String -> s -> m (Either ParcomError a, ParcomState s)
+runParserT p fn str =
+    runParcomT p state
     where
         state =
             ParcomState
                 { psSourcePosition = SourcePosition fn 1 1
                 , psStream = str }
 
+parseT :: (Stream s t, Token t, Monad m) => ParcomT s t m a -> String -> s -> m (Either ParcomError a)
+parseT p fn str = fst `liftM` runParserT p fn str
+
 parse :: (Stream s t, Token t) => Parcom s t a -> String -> s -> Either ParcomError a
-parse p fn str = fst $ runParser p fn str
+parse p fn str = runIdentity $ parseT p fn str
 
-getState :: Parcom s t (ParcomState s)
-getState = Parcom $ \s -> (Right s, s)
+getState :: Monad m => ParcomT s t m (ParcomState s)
+getState = ParcomT $ \s -> return (Right s, s)
 
-useState :: (ParcomState s -> a) -> Parcom s t a
-useState f = Parcom $ \s -> (Right $ f s, s)
+useState :: Monad m => (ParcomState s -> a) -> ParcomT s t m a
+useState f = ParcomT $ \s -> return (Right $ f s, s)
 
-setState :: ParcomState s -> Parcom s t ()
-setState s = Parcom $ \_ -> (Right (), s)
+setState :: Monad m => ParcomState s -> ParcomT s t m ()
+setState s = ParcomT $ \_ -> return (Right (), s)
 
-modifyState :: (ParcomState s -> ParcomState s) -> Parcom s t ()
-modifyState f = Parcom $ \s -> (Right (), f s)
+modifyState :: Monad m => (ParcomState s -> ParcomState s) -> ParcomT s t m ()
+modifyState f = ParcomT $ \s -> return (Right (), f s)
 
-handle :: Parcom s t a -> (ParcomError -> Parcom s t b) -> (a -> Parcom s t b) -> Parcom s t b
-handle p f t = Parcom $ \s ->
-    let (r', s') = runParcom p s
-    in case r' of
-        -- parse failed: run the error handler
-        Left e -> runParcom (f e) s'
-        -- parse succeeded: run the success handler
-        Right x -> runParcom (t x) s'
+-- | Very general error / success handler
+-- Each of the error / success branches takes both the old and the new state,
+-- so that the branch handler itself can decide whether to backtrack or
+-- continue with the new state.
+handle' :: Monad m
+    => ParcomT s t m a
+    -> (ParcomError -> ParcomState s -> ParcomState s -> m (Either ParcomError b, ParcomState s))
+    -> (a -> ParcomState s -> ParcomState s -> m (Either ParcomError b, ParcomState s))
+    -> ParcomT s t m b
+handle' p errorH successH = ParcomT $ \s -> do
+    (r, s') <- runParcomT p s
+    case r of
+        -- parse failed: run error handler
+        Left e -> errorH e s s'
+        -- parse succeeded: run success handler
+        Right x -> successH x s s'
 
+handle :: Monad m
+    => ParcomT s t m a
+    -> (ParcomError -> ParcomT s t m b)
+    -> (a -> ParcomT s t m b)
+    -> ParcomT s t m b
+handle p f t =
+    handle' p (\e _ s' -> runParcomT (f e) s') (\x _ s' -> runParcomT (t x) s')
 
+handleB :: Monad m
+    => ParcomT s t m a
+    -> (ParcomError -> ParcomT s t m b)
+    -> (a -> ParcomT s t m b)
+    -> ParcomT s t m b
+handleB p f t =
+    handle' p (\e s _ -> runParcomT (f e) s) (\x s _ -> runParcomT (t x) s)
+
 -- | Backtracking modifier; restores the parser state to the previous situation
 -- if the wrapped parser fails.
-try :: Parcom s t a -> Parcom s t a
-try p = Parcom $ \s ->
-    let (r', s') = runParcom p s
-    in case r' of
-        -- parse failed: return the error and restore the old state
-        Left e -> (Left e, s)
-        -- parse succeeded: return the result and the new state
-        Right x -> (Right x, s')
+try :: Monad m => ParcomT s t m a -> ParcomT s t m a
+try p = handle' p (\e s _ -> return (Left e, s)) (\x _ s' -> return (Right x, s'))
 
 -- | Return the result of the first parser that succeeds.
-alt :: Parcom s t a -> Parcom s t a -> Parcom s t a
-alt a b = Parcom $ \s ->
-            let (r', s') = runParcom a s
-            in case r' of
-                Left _ -> runParcom b s
-                Right x -> (Right x, s')
+alt :: Monad m => ParcomT s t m a -> ParcomT s t m a -> ParcomT s t m a
+alt a b = handle' a (\e s _ -> runParcomT b s) (\x _ s' -> return (Right x, s'))
 
 -- | Succeeds iff the given parser fails
-notFollowedBy :: (Stream s t) => Parcom s t a -> Parcom s t ()
-notFollowedBy p = Parcom $ \s ->
-    let (r', s') = runParcom p s
-    in case r' of
-        Left _ -> (Right (), s)
-        Right x -> runParcom (fail "something followed that shouldn't") s
+notFollowedBy :: (Monad m, Stream s t) => ParcomT s t m a -> ParcomT s t m ()
+notFollowedBy p = handle' p (\_ s _ -> return (Right (), s)) (\x s _ -> runParcomT (fail "something followed that shouldn't") s)
     
 -- | Gets the next token from the stream without consuming it.
 -- Fails at end-of-input.
-peek :: (Stream s t) => Parcom s t t
+peek :: (Monad m, Stream s t) => ParcomT s t m t
 peek = do
     str <- psStream `liftM` getState
     if Stream.atEnd str
@@ -131,7 +152,7 @@
         else return (Stream.peek str)
 
 -- | Checks whether end-of-input has been reached.
-atEnd :: (Stream s t) => Parcom s t Bool
+atEnd :: (Monad m, Stream s t) => ParcomT s t m Bool
 atEnd = useState (Stream.atEnd . psStream)
 
 nextLine :: SourcePosition -> SourcePosition
@@ -144,7 +165,7 @@
 
 -- | Gets the next token from the stream and consumes it.
 -- Fails at end-of-input.
-next :: (Stream s t, Token t) => Parcom s t t
+next :: (Monad m, Stream s t, Token t) => ParcomT s t m t
 next = do
     str <- psStream `liftM` getState
     if Stream.atEnd str
@@ -157,5 +178,5 @@
                 else modifyState $ \s -> s { psSourcePosition = nextColumn (psSourcePosition s) }
             return t
 
-(<?>) :: (Stream s t) => Parcom s t a -> String -> Parcom s t a
+(<?>) :: (Monad m, Stream s t) => ParcomT s t m a -> String -> ParcomT s t m a
 p <?> expected = p <|> fail ("Expected " ++ expected)
diff --git a/Text/Parcom/Prim.hs b/Text/Parcom/Prim.hs
--- a/Text/Parcom/Prim.hs
+++ b/Text/Parcom/Prim.hs
@@ -9,20 +9,20 @@
 import Text.Parcom.Internal
 
 -- | Gets the next token from the stream
-anyToken :: (Stream s t, Token t) => Parcom s t t
+anyToken :: (Monad m, Stream s t, Token t) => ParcomT s t m t
 anyToken = next
 
 -- | Succeeds iff end-of-input has been reached
-eof :: (Stream s t, Token t) => Parcom s t ()
+eof :: (Monad m, Stream s t, Token t) => ParcomT s t m ()
 eof = notFollowedBy anyToken <?> "end of input"
 
 -- | Matches one token against a list of possible tokens; returns the
 -- matching token.
-oneOf :: (Stream s t, Token t, Show t, Eq t) => [t] -> Parcom s t t
+oneOf :: (Monad m, Stream s t, Token t, Show t, Eq t) => [t] -> ParcomT s t m t
 oneOf xs = satisfy (`elem` xs) <?> (formatOptionList . map show) xs
 
 -- | Succeeds if the given predicate is met for the next token.
-satisfy :: (Stream s t, Token t) => (t -> Bool) -> Parcom s t t
+satisfy :: (Monad m, Stream s t, Token t) => (t -> Bool) -> ParcomT s t m t
 satisfy p = do
     c <- peek
     if p c
@@ -30,11 +30,11 @@
         else fail "Predicate not met"
 
 -- | Exactly match one particular token
-token :: (Stream s t, Token t, Show t, Eq t) => t -> Parcom s t t
+token :: (Monad m, Stream s t, Token t, Show t, Eq t) => t -> ParcomT s t m t
 token t = satisfy (== t) <?> show t
 
 -- | Match a series of tokens exactly
-tokens :: (Stream s t, Token t, Eq t, Show t) => [t] -> Parcom s t [t]
+tokens :: (Monad m, Stream s t, Token t, Eq t, Show t) => [t] -> ParcomT s t m [t]
 tokens [] = return []
 tokens (x:xs) = do
     c <- token x
diff --git a/parcom-lib.cabal b/parcom-lib.cabal
--- a/parcom-lib.cabal
+++ b/parcom-lib.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                parcom-lib
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            A simple parser-combinator library, a bit like Parsec but without the frills
 description:         Parcom provides parser combinator functionality in a string-type-agnostic way;
                      it supports strict ByteStrings (with Word8 tokens) and any list type (with
@@ -26,3 +26,5 @@
                , containers ==0.4.*
                , bytestring ==0.9.*
                , word8
+               , mtl == 2.1.*
+               , transformers == 0.3.*
