diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+1.0.1 (2013/01/23)
+ - Changed type of transformer from [Ast] -> [Ast] to [Ast] -> IO [Ast]
+   (as suggested by John MacFarlane)
+
 0.1.2 (2011/01/30)
  - Updated for GHC 7 (Thanks to Yusaku HASHIMOTO)
 
diff --git a/Language/Haskell/Preprocessor.hs b/Language/Haskell/Preprocessor.hs
--- a/Language/Haskell/Preprocessor.hs
+++ b/Language/Haskell/Preprocessor.hs
@@ -15,6 +15,7 @@
 import System.Environment (getProgName)
 import System.Exit (exitFailure)
 import Control.Exception (bracket)
+import Control.Monad ((<=<))
 
 import Data.Monoid (Monoid(..))
 import qualified Control.Monad.Writer as W
@@ -29,34 +30,36 @@
 
 data Extension = Extension {
                    keywords    :: [[Keyword]],
-                   transformer :: [Ast] -> [Ast],
+                   transformer :: [Ast] -> IO [Ast],
                    synspec     :: SynSpec,
                    usage       :: Maybe (IO ()),
                    syntaxerror :: Maybe (E.Error -> IO ())
                  }
 
+instance Semigroup Extension where
+  e1 <> e2 = Extension {
+               keywords    = keywords e1 ++ keywords e2,
+               transformer = transformer e1 <=< transformer e2,
+               synspec     = synspec e1 `mappend` synspec e2,
+               usage       = usage e1       <+ usage e2,
+               syntaxerror = syntaxerror e1 <+ syntaxerror e2
+             }
+    where Just a  <+ _ = Just a
+          Nothing <+ b = b
+
 instance Monoid Extension where
   mempty          = Extension {
                       keywords    = [],
-                      transformer = id,
+                      transformer = return,
                       synspec     = mempty,
                       usage       = Nothing,
                       syntaxerror = Nothing
                     }
-  e1 `mappend` e2 = Extension {
-                      keywords    = keywords e1 ++ keywords e2,
-                      transformer = transformer e1 . transformer e2,
-                      synspec     = synspec e1 `mappend` synspec e2,
-                      usage       = usage e1       <+ usage e2,
-                      syntaxerror = syntaxerror e1 <+ syntaxerror e2
-                    }
-    where Just a  <+ _ = Just a
-          Nothing <+ b = b
 
 base :: Extension
 base  = Extension {
           keywords    = [],
-          transformer = id,
+          transformer = return,
           synspec     = defaultSpec,
           usage       = Just (do
             prog <- getProgName
@@ -82,7 +85,7 @@
                       Nothing -> return ()
                     exitFailure
       Right r -> return r
-    let result = transformer extension asts
+    result <- transformer extension asts
     case files of
       [_, _, file] -> fileDump spec file result
       _            -> stdoutDump spec result
diff --git a/Language/Haskell/Preprocessor/Ast.hs b/Language/Haskell/Preprocessor/Ast.hs
--- a/Language/Haskell/Preprocessor/Ast.hs
+++ b/Language/Haskell/Preprocessor/Ast.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Language.Haskell.Preprocessor.Ast (
   Ast(..), flatten, flattenList, format, cons,
   module Language.Haskell.Preprocessor.Token
diff --git a/Language/Haskell/Preprocessor/Error.hs b/Language/Haskell/Preprocessor/Error.hs
--- a/Language/Haskell/Preprocessor/Error.hs
+++ b/Language/Haskell/Preprocessor/Error.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Language.Haskell.Preprocessor.Error (
   Error(..), fromParseError, errorAt
 ) where
diff --git a/Language/Haskell/Preprocessor/Loc.hs b/Language/Haskell/Preprocessor/Loc.hs
--- a/Language/Haskell/Preprocessor/Loc.hs
+++ b/Language/Haskell/Preprocessor/Loc.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Language.Haskell.Preprocessor.Loc (
   Loc, file, line, col,
   initial, bogus, isBogus,
diff --git a/Language/Haskell/Preprocessor/Printer.hs b/Language/Haskell/Preprocessor/Printer.hs
--- a/Language/Haskell/Preprocessor/Printer.hs
+++ b/Language/Haskell/Preprocessor/Printer.hs
@@ -1,10 +1,9 @@
 module Language.Haskell.Preprocessor.Printer ( dump ) where
 
 import Language.Haskell.Preprocessor.Ast
-import Language.Haskell.Preprocessor.Token
 import Language.Haskell.Preprocessor.Loc
 
-import Control.Monad (foldM)
+import Control.Monad (foldM, foldM_)
 
 dump :: Monad m => (String -> m ()) -> [Ast] -> m ()
 dump write forest = start where
@@ -15,7 +14,7 @@
                   x:_ -> initial (file (loc x))
     write (toDirective here)
     write "\n"
-    foldM sayToken here items
+    foldM_ sayToken here items
     write "\n"
 
   sayToken here item
diff --git a/Language/Haskell/Preprocessor/SynSpec.hs b/Language/Haskell/Preprocessor/SynSpec.hs
--- a/Language/Haskell/Preprocessor/SynSpec.hs
+++ b/Language/Haskell/Preprocessor/SynSpec.hs
@@ -13,18 +13,20 @@
                }
   deriving (Eq, Show)
 
+instance Semigroup SynSpec where
+  s1 <> s2 = SynSpec {
+               unboxed   = unboxed s1   || unboxed s2,
+               pragmas   = pragmas s1   || pragmas s2,
+               levelnest = levelnest s1 || levelnest s2,
+               blocks    = blocks s1    ++ blocks s2
+             }
+
 instance Monoid SynSpec where
   mempty          = SynSpec {
                       unboxed   = False,
                       pragmas   = False,
                       levelnest = False,
                       blocks    = []
-                    }
-  s1 `mappend` s2 = SynSpec {
-                      unboxed   = unboxed s1   || unboxed s2,
-                      pragmas   = pragmas s1   || pragmas s2,
-                      levelnest = levelnest s1 || levelnest s2,
-                      blocks    = blocks s1    ++ blocks s2
                     }
 
 data Keyword = I { getKey :: String }
diff --git a/Language/Haskell/Preprocessor/Token.hs b/Language/Haskell/Preprocessor/Token.hs
--- a/Language/Haskell/Preprocessor/Token.hs
+++ b/Language/Haskell/Preprocessor/Token.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Language.Haskell.Preprocessor.Token (
   Token(..), Tag(..), newToken
 ) where
diff --git a/preprocessor-tools.cabal b/preprocessor-tools.cabal
--- a/preprocessor-tools.cabal
+++ b/preprocessor-tools.cabal
@@ -1,16 +1,16 @@
 Name:           preprocessor-tools
-Version:        0.1.3
+Version:        2.0.2
 Cabal-Version:  >= 1.2.3
 License:        BSD3
 License-File:   LICENSE
 Stability:      experimental
-Author:         Jesse A. Tov <tov@ccs.neu.edu>
-Maintainer:     tov@ccs.neu.edu
-Homepage:       http://www.ccs.neu.edu/~tov/pubs/haskell-session-types
+Author:         Jesse A. Tov <jesse@eecs.northwestern.edu>
+Maintainer:     jesse@eecs.northwestern.edu
+Homepage:       https://github.com/tov/preprocessor-tools-hs
 Category:       Source-tools, Language, Code Generation
 Synopsis:       A framework for extending Haskell's syntax via quick-and-dirty preprocessors
 Build-type:     Simple
-Tested-with:    GHC == 7.4.1
+Tested-with:    GHC == 8.4.2
 Description:
         This library provides a quick-and-dirty (but often effective)
         method for extending Haskell's syntax using a custom
@@ -28,10 +28,9 @@
 Library
   Build-Depends:        mtl >=1 && <3,
                         parsec >=3 && <4,
-                        base >=4 && <5,
+                        base >=4.11 && <5,
                         syb >=0 && <1
 
-  Extensions:           DeriveDataTypeable
   Exposed-modules:
     Language.Haskell.Preprocessor,
     Language.Haskell.Preprocessor.Ast,
@@ -44,3 +43,4 @@
     Language.Haskell.Preprocessor.Util
   Other-modules:
     Language.Haskell.Preprocessor.Lexer
+  GHC-options: -Wall -Wno-name-shadowing
