diff --git a/Data/Aeson.hs b/Data/Aeson.hs
--- a/Data/Aeson.hs
+++ b/Data/Aeson.hs
@@ -1,3 +1,4 @@
+-- |
 -- Module:      Data.Aeson
 -- Copyright:   (c) 2011 MailRank, Inc.
 -- License:     Apache
diff --git a/Data/Aeson/Encode.hs b/Data/Aeson/Encode.hs
--- a/Data/Aeson/Encode.hs
+++ b/Data/Aeson/Encode.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 
+-- |
 -- Module:      Data.Aeson.Encode
 -- Copyright:   (c) 2011 MailRank, Inc.
 -- License:     Apache
diff --git a/Data/Aeson/Generic.hs b/Data/Aeson/Generic.hs
--- a/Data/Aeson/Generic.hs
+++ b/Data/Aeson/Generic.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE PatternGuards, RankNTypes, ScopedTypeVariables  #-}
 
+-- |
 -- Module:      Data.Aeson.Generic
 -- Copyright:   (c) 2011 MailRank, Inc.
 --              (c) 2008, 2009 Lennart Augustsson
diff --git a/Data/Aeson/Parser.hs b/Data/Aeson/Parser.hs
--- a/Data/Aeson/Parser.hs
+++ b/Data/Aeson/Parser.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 
+-- |
 -- Module:      Data.Aeson.Parser
 -- Copyright:   (c) 2011 MailRank, Inc.
 -- License:     Apache
diff --git a/Data/Aeson/Types.hs b/Data/Aeson/Types.hs
--- a/Data/Aeson/Types.hs
+++ b/Data/Aeson/Types.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DeriveDataTypeable, FlexibleInstances, GeneralizedNewtypeDeriving,
     IncoherentInstances, OverlappingInstances, Rank2Types #-}
 
+-- |
 -- Module:      Data.Aeson.Types
 -- Copyright:   (c) 2011 MailRank, Inc.
 -- License:     Apache
@@ -28,6 +29,8 @@
     , FromJSON(..)
     , fromJSON
     , parse
+    , parseEither
+    , parseMaybe
     , ToJSON(..)
     -- * Constructors and accessors
     , (.=)
@@ -38,7 +41,7 @@
 
 import Control.Applicative
 import Control.DeepSeq (NFData(..))
-import Control.Monad (MonadPlus(..))
+import Control.Monad (MonadPlus(..), ap)
 import Data.Aeson.Functions
 import Data.Attoparsec.Char8 (Number(..))
 import Data.Data (Data)
@@ -71,17 +74,58 @@
               | Success a
                 deriving (Eq, Show, Typeable)
 
--- | Failure continuation.  Constructs an 'Error'.
-type Failure r   = String -> Result r
--- | Success continuation.  Constructs a 'Success'.
-type Success a r = a -> Result r
+instance (NFData a) => NFData (Result a) where
+    rnf (Success a) = rnf a
+    rnf (Error err) = rnf err
 
+instance Functor Result where
+    fmap f (Success a) = Success (f a)
+    fmap _ (Error err) = Error err
+    {-# INLINE fmap #-}
+
+instance Monad Result where
+    return = Success
+    {-# INLINE return #-}
+    Success a >>= k = k a
+    Error err >>= _ = Error err
+    {-# INLINE (>>=) #-}
+
+instance Applicative Result where
+    pure  = return
+    {-# INLINE pure #-}
+    (<*>) = ap
+    {-# INLINE (<*>) #-}
+
+instance MonadPlus Result where
+    mzero = fail "mzero"
+    {-# INLINE mzero #-}
+    mplus a@(Success _) _ = a
+    mplus _ b             = b
+    {-# INLINE mplus #-}
+
+instance Alternative Result where
+    empty = mzero
+    {-# INLINE empty #-}
+    (<|>) = mplus
+    {-# INLINE (<|>) #-}
+
+instance Monoid (Result a) where
+    mempty  = fail "mempty"
+    {-# INLINE mempty #-}
+    mappend = mplus
+    {-# INLINE mappend #-}
+
+-- | Failure continuation.
+type Failure f r   = String -> f r
+-- | Success continuation.
+type Success a f r = a -> f r
+
 -- | A continuation-based parser type.
 newtype Parser a = Parser {
-      runParser :: forall r.
-                   Failure r
-                -> Success a r
-                -> Result r
+      runParser :: forall f r.
+                   Failure f r
+                -> Success a f r
+                -> f r
     }
 
 instance Monad Parser where
@@ -182,6 +226,16 @@
 parse :: (a -> Parser b) -> a -> Result b
 parse m v = runParser (m v) Error Success
 {-# INLINE parse #-}
+
+-- | Run a 'Parser' with a 'Maybe' result type.
+parseMaybe :: (a -> Parser b) -> a -> Maybe b
+parseMaybe m v = runParser (m v) (const Nothing) Just
+{-# INLINE parseMaybe #-}
+
+-- | Run a 'Parser' with an 'Either' result type.
+parseEither :: (a -> Parser b) -> a -> Either String b
+parseEither m v = runParser (m v) Left Right
+{-# INLINE parseEither #-}
 
 -- | Retrieve the value associated with the given key of an 'Object'.
 -- The result is 'empty' if the key is not present or the value cannot
diff --git a/aeson.cabal b/aeson.cabal
--- a/aeson.cabal
+++ b/aeson.cabal
@@ -1,5 +1,5 @@
 name:            aeson
-version:         0.3.2.1
+version:         0.3.2.2
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Web, JSON
@@ -75,6 +75,8 @@
     benchmarks/json-data/twitter20.json
     benchmarks/json-data/twitter50.json
     benchmarks/json-data/twitter100.json
+    tests/Makefile
+    tests/Properties.hs
 
 flag developer
   description: operate in developer mode
@@ -99,13 +101,12 @@
     base == 4.*,
     blaze-builder >= 0.2.1.4,
     bytestring,
-    bytestring-show,
     containers,
     deepseq,
     ghc-prim,
     hashable,
     integer-gmp,
-    monads-fd,
+    mtl,
     old-locale,
     syb,
     text >= 0.11.0.2,
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,9 @@
+ghc := ghc
+
+all: qc
+
+qc: Properties.hs
+	$(ghc) -o --make $@ $<
+
+clean:
+	-rm -f qc *.o *.hi
diff --git a/tests/Properties.hs b/tests/Properties.hs
new file mode 100644
--- /dev/null
+++ b/tests/Properties.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+import Data.Aeson.Encode
+import Data.Aeson.Parser (value)
+import Data.Aeson.Types
+import Data.Attoparsec.Number
+import Test.Framework (defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.QuickCheck (Arbitrary(..))
+import Test.QuickCheck.Monadic (assert, monadicIO, run)
+import qualified Data.ByteString.Lazy.Char8 as L
+import qualified Data.Attoparsec.Lazy as L
+
+encodeDouble d  = encode (Number (D d)) == L.pack (show d)
+encodeInteger i = encode (Number (I i)) == L.pack (show i)
+
+roundTrip :: (Eq a, FromJSON a, ToJSON a) => a -> Bool
+roundTrip i = (fmap fromJSON . L.maybeResult . L.parse value . encode . toJSON) i == Just (Success i)
+
+roundTripBool (v::Bool) = roundTrip v
+roundTripDouble (v::Double) = roundTrip v
+roundTripInteger (v::Integer) = roundTrip v
+
+main = defaultMain tests
+
+tests = [
+  testGroup "encode" [
+      testProperty "encodeDouble" encodeDouble
+    , testProperty "encodeInteger" encodeInteger
+    ],
+  testGroup "roundTrip" [
+      testProperty "roundTripBool" roundTripBool
+    , testProperty "roundTripDouble" roundTripDouble
+    , testProperty "roundTripInteger" roundTripInteger
+    ]
+  ]
