diff --git a/Control/Monad/Attempt.hs b/Control/Monad/Attempt.hs
new file mode 100644
--- /dev/null
+++ b/Control/Monad/Attempt.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+---------------------------------------------------------
+--
+-- Module        : Control.Monad.Attempt
+-- Copyright     : Michael Snoyman
+-- License       : BSD3
+--
+-- Maintainer    : Michael Snoyman <michael@snoyman.com>
+-- Stability     : Unstable
+-- Portability   : portable
+--
+---------------------------------------------------------
+
+-- | Provide a monad transformer for the attempt monad, which allows the
+-- reporting of errors using extensible exceptions.
+module Control.Monad.Attempt
+    ( AttemptT (..)
+    , evalAttemptT
+    , module Data.Attempt
+    ) where
+
+import Data.Attempt
+import Control.Applicative
+import Control.Monad
+import Control.Monad.Trans
+
+newtype AttemptT m v = AttemptT {
+    runAttemptT :: m (Attempt v)
+}
+
+instance Monad m => Functor (AttemptT m) where
+    fmap f = AttemptT . liftM (liftM f) . runAttemptT
+instance Monad m => Applicative (AttemptT m) where
+    pure = return
+    (<*>) = ap
+instance Monad m => Monad (AttemptT m) where
+    return = AttemptT . return . Success
+    (AttemptT mv) >>= f = AttemptT $
+        mv >>= attempt (return . Failure) (runAttemptT . f)
+instance Monad m => MonadAttempt (AttemptT m) where
+    failure = AttemptT . return . Failure
+    wrapFailure f (AttemptT mv) = AttemptT $ liftM (wrapFailure f) mv
+instance MonadTrans AttemptT where
+    lift = AttemptT . liftM Success where
+instance MonadIO m => MonadIO (AttemptT m) where
+    liftIO = AttemptT . liftM Success . liftIO where
+instance Monad m => FromAttempt (AttemptT m) where
+    fromAttempt = attempt failure return
+
+-- | Instances of 'FromAttempt' specify a manner for embedding 'Attempt'
+-- failures directly into the target data type. For example, the 'IO' instance
+-- simply throws a runtime error. This is a convenience wrapper when you simply
+-- want to use that default action.
+--
+-- So given a type 'AttemptT' 'IO' 'Int', this function will convert it to 'IO'
+-- 'Int', throwing any exceptions in the original value.
+evalAttemptT :: (Monad m, FromAttempt m)
+             => AttemptT m v
+             -> m v
+evalAttemptT = join . liftM fromAttempt . runAttemptT where
diff --git a/Control/Monad/Attempt/Class.hs b/Control/Monad/Attempt/Class.hs
new file mode 100644
--- /dev/null
+++ b/Control/Monad/Attempt/Class.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE Rank2Types #-}
+---------------------------------------------------------
+--
+-- Module        : Control.Monad.Attempt.Class
+-- Copyright     : Michael Snoyman
+-- License       : BSD3
+--
+-- Maintainer    : Michael Snoyman <michael@snoyman.com>
+-- Stability     : Unstable
+-- Portability   : portable
+--
+---------------------------------------------------------
+
+-- | Defines a type class for any monads which may report failure using
+-- extensible exceptions.
+module Control.Monad.Attempt.Class
+    ( MonadAttempt (..)
+    , StringException (..)
+    ) where
+
+import Control.Exception
+import Data.Generics
+
+-- | Any 'Monad' which may report failure using extensible exceptions. This
+-- most obviously applies to the Attempt data type, but you should just as well
+-- use this for arbitrary 'Monad's.
+--
+-- Usage should be straight forward: 'return' successes and 'failure' errors.
+-- If you simply want to send a string error message, use 'failureString'.
+-- Although tempting to do so, 'fail' is *not* used as a synonym for
+-- 'failureString'; 'fail' should not be used at all.
+--
+-- Minimal complete definition: 'failure' and 'wrapFailure'.
+class Monad m => MonadAttempt m where
+    failure :: Exception e => e -> m v
+
+    -- | Call 'failure' by wrapping the argument in a 'StringException'.
+    failureString :: String -> m v
+    failureString = failure . StringException
+
+    -- | Wrap the failure value, if any, with the given function. This is
+    -- useful in particular when you want all the exceptions returned from a
+    -- certain library to be of a certain type, even if they were generated by
+    -- a different library.
+    wrapFailure :: Exception eOut
+                => (forall eIn. Exception eIn => eIn -> eOut)
+                -> m v
+                -> m v
+
+-- | A simple exception which simply contains a string. Note that the 'Show'
+-- instance simply returns the contained string.
+newtype StringException = StringException String
+    deriving Typeable
+instance Show StringException where
+    show (StringException s) = s
+instance Exception StringException
diff --git a/Data/Attempt.hs b/Data/Attempt.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attempt.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE Rank2Types #-}
+---------------------------------------------------------
+--
+-- Module        : Data.Attempt
+-- Copyright     : Michael Snoyman
+-- License       : BSD3
+--
+-- Maintainer    : Michael Snoyman <michael@snoyman.com>
+-- Stability     : Unstable
+-- Portability   : portable
+--
+-- A universal data type for computations which may fail.
+---------------------------------------------------------
+
+-- | A universal data type for computations which may fail. Errors are reported
+-- using extensible exceptions. These exceptions are not explicitly stated; if
+-- you want this kind of functionality, something like control-monad-exception
+-- might be a more appropriate fit.
+module Data.Attempt
+    ( Attempt (..)
+    , FromAttempt (..)
+    , fa
+    , attempt
+    , makeHandler
+    , AttemptHandler
+    , module Control.Monad.Attempt.Class
+    ) where
+
+import qualified Control.Exception as E
+import Control.Monad (ap)
+import Control.Applicative
+import Data.Generics
+import Control.Monad.Attempt.Class
+
+-- | Contains either a 'Success' value or a 'Failure' exception.
+data Attempt v =
+    Success v
+    | forall e. E.Exception e => Failure e
+    deriving (Typeable)
+
+instance Show v => Show (Attempt v) where
+    show (Success v) = "Success " ++ show v
+    show (Failure e) = "Failure " ++ show e
+
+instance Functor Attempt where
+    fmap f (Success v) = Success $ f v
+    fmap _ (Failure e) = Failure e
+instance Applicative Attempt where
+    pure = Success
+    (<*>) = ap
+instance Monad Attempt where
+    return = Success
+    (Success v) >>= f = f v
+    (Failure e) >>= _ = Failure e
+instance MonadAttempt Attempt where
+    failure = Failure
+    wrapFailure _ (Success v) = Success v
+    wrapFailure f (Failure e) = Failure $ f e
+
+-- | Any type which can be converted from an 'Attempt'. The included instances are your \"usual suspects\" for dealing with error handling. They include:
+--
+-- 'IO': For the IO instance, any exceptions in the 'Attempt' are thrown as
+-- runtime exceptions.
+--
+-- 'Maybe': Returns 'Nothing' on 'Failure', or 'Just' on 'Success'.
+--
+-- List: Returns the empty list on 'Failure', or a singleton list on 'Success'.
+--
+-- 'Either' 'String': Returns 'Left' ('show' exception) on 'Failure', or 'Right' on
+-- 'Success'.
+--
+-- 'Either' 'E.Exception': Returns 'Left' exception on 'Failure', or 'Right' on
+-- 'Success'.
+class FromAttempt a where
+    fromAttempt :: Attempt v -> a v
+
+-- | A shortcut for 'fromAttempt'.
+fa :: FromAttempt a => Attempt v -> a v
+fa = fromAttempt
+
+instance FromAttempt IO where
+    fromAttempt = attempt E.throwIO return
+instance FromAttempt Maybe where
+    fromAttempt = attempt (const Nothing) Just
+instance FromAttempt [] where
+    fromAttempt = attempt (const []) (: [])
+instance FromAttempt (Either String) where
+    fromAttempt = attempt (Left . show) Right
+instance FromAttempt (Either E.SomeException) where
+    fromAttempt = attempt (Left . E.SomeException) Right
+
+-- | Process either the exception or value in an 'Attempt' to produce a result.
+--
+-- This function is modeled after 'maybe' and 'either'. The first argument must
+-- accept any instances of 'E.Exception'. If you want to handle multiple types
+-- of exceptions, see 'makeHandler'. The second argument converts the success
+-- value.
+attempt :: (forall e. E.Exception e => e -> b) -- ^ error handler
+        -> (a -> b) -- ^ success handler
+        -> Attempt a
+        -> b
+attempt _ f (Success v) = f v
+attempt f _ (Failure e) = f e
+
+-- | Convert multiple 'AttemptHandler's and a default value into an exception
+-- handler.
+--
+-- This is a convenience function when you want to have special handling for a
+-- few types of 'E.Exception's and provide another value for anything else.
+makeHandler :: [AttemptHandler v] -> v -> (forall e. E.Exception e => e -> v)
+makeHandler [] v _ = v
+makeHandler (AttemptHandler h:hs) v e =
+    case cast e of
+        Nothing -> makeHandler hs v e
+        Just e' -> h e'
+
+-- | A simple wrapper value necesary due to the Haskell type system. Wraps a
+-- function from a *specific* 'E.Exception' type to some value.
+data AttemptHandler v = forall e. E.Exception e => AttemptHandler (e -> v)
diff --git a/Data/Attempt/Helper.hs b/Data/Attempt/Helper.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attempt/Helper.hs
@@ -0,0 +1,132 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+---------------------------------------------------------
+--
+-- Module        : Data.Attempt.Helper
+-- Copyright     : Michael Snoyman
+-- License       : BSD3
+--
+-- Maintainer    : Michael Snoyman <michael@snoyman.com>
+-- Stability     : Unstable
+-- Portability   : portable
+---------------------------------------------------------
+
+-- | Replacements for standard functions to represent failure with a
+-- 'MonadAttempt'.  Lots of inspiration taken from the "safe" package.
+module Data.Attempt.Helper
+    ( -- * Non-standard functions
+      join
+      -- * Exception types
+    , KeyNotFound (..)
+    , EmptyList (..)
+    , CouldNotRead (..)
+    , NegativeIndex (..)
+      -- * Standard functions reimplemented
+    , lookup
+    , tail
+    , init
+    , head
+    , last
+    , read
+    , at
+    , assert
+      -- * IO functions with exceptions handled
+    , readFile
+    ) where
+
+import Prelude hiding (lookup, tail, init, head, last, read, readFile)
+import qualified Prelude
+import Data.Attempt
+import Control.Monad.Attempt.Class
+import Data.Generics
+import qualified Control.Exception as E
+import Control.Monad (liftM)
+import Control.Monad.Trans
+
+-- | This is not a simple translation of the Control.Monad.join function.
+-- Instead, for 'Monad's which are instances of 'FromAttempt', it removes the
+-- inner 'Attempt' type, reporting errors as defined in the 'FromAttempt'
+-- instance.
+--
+-- For example, join (Just (failureString \"foo\")) == Nothing.
+join :: (FromAttempt m, Monad m) => m (Attempt v) -> m v
+join = (>>= fromAttempt)
+
+-- | Exception type for the 'lookup' function.
+data KeyNotFound k v = KeyNotFound k [(k, v)]
+    deriving Typeable
+instance Show k => Show (KeyNotFound k v) where
+    show (KeyNotFound key _) = "Could not find requested key: " ++ show key
+instance (Typeable k, Typeable v, Show k) => E.Exception (KeyNotFound k v)
+
+lookup :: (Typeable k, Typeable v, Show k, Eq k, MonadAttempt m)
+       => k
+       -> [(k, v)]
+       -> m v
+lookup k m = maybe (failure $ KeyNotFound k m) return $ Prelude.lookup k m
+
+-- | Exception type for functions which expect non-empty lists.
+data EmptyList = EmptyList
+    deriving (Show, Typeable)
+instance E.Exception EmptyList
+
+tail :: MonadAttempt m => [a] -> m [a]
+tail [] = failure EmptyList
+tail (_:rest) = return rest
+
+init :: MonadAttempt m => [a] -> m [a]
+init [] = failure EmptyList
+init x = return $ Prelude.init x
+
+head :: MonadAttempt m => [a] -> m a
+head [] = failure EmptyList
+head (x:_) = return x
+
+last :: MonadAttempt m => [a] -> m a
+last [] = failure EmptyList
+last x = return $ Prelude.last x
+
+-- | Report errors from the 'read' function.
+newtype CouldNotRead = CouldNotRead String
+    deriving (Typeable, Show)
+instance E.Exception CouldNotRead
+
+read :: (MonadAttempt m, Read a) => String -> m a
+read s = case [x | (x,t) <- reads s, ("","") <- lex t] of
+            [x] -> return x
+            _ -> failure $ CouldNotRead s
+
+-- | For functions which expect index values >= 0.
+data NegativeIndex = NegativeIndex
+    deriving (Typeable, Show)
+instance E.Exception NegativeIndex
+data OutOfBoundsIndex = OutOfBoundsIndex
+    deriving (Typeable, Show)
+instance E.Exception OutOfBoundsIndex
+
+-- | Same as Prelude.!!. Name stolen from safe library.
+at :: MonadAttempt m => [a] -> Int -> m a
+at [] _ = failure OutOfBoundsIndex
+at (x:_) 0 = return x
+at (_:xs) n
+    | n < 0 = failure NegativeIndex
+    | otherwise = at xs $ n - 1
+
+-- | Assert a value to be true. If true, returns the first value as a succss.
+-- Otherwise, returns the second value as a failure.
+assert :: (MonadAttempt m, E.Exception e)
+       => Bool
+       -> v
+       -> e
+       -> m v
+assert b v e = if b then return v else failure e
+
+-- | The standard readFile function with any 'E.IOException's returned as a
+-- failure instead of a runtime exception.
+readFile :: (MonadAttempt m, MonadIO m) => FilePath -> m String
+readFile fp = do
+    contents <- liftIO $ E.handle
+                    (\e -> return $ Left (e :: E.IOException))
+                    (liftM Right $ Prelude.readFile fp)
+    case contents of
+        Left e -> failure e
+        Right v -> return v
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+The following license covers this documentation, and the source code, except
+where otherwise indicated.
+
+Copyright 2008, Michael Snoyman. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,11 @@
+#!/usr/bin/env runhaskell
+
+> module Main where
+> import Distribution.Simple
+> import System.Cmd (system)
+
+> main :: IO ()
+> main = defaultMainWithHooks (simpleUserHooks { runTests = runTests' })
+
+> runTests' :: a -> b -> c -> d -> IO ()
+> runTests' _ _ _ _ = system "runhaskell Test.hs" >> return ()
diff --git a/attempt.cabal b/attempt.cabal
new file mode 100644
--- /dev/null
+++ b/attempt.cabal
@@ -0,0 +1,23 @@
+name:            attempt
+version:         0.0.0
+license:         BSD3
+license-file:    LICENSE
+author:          Michael Snoyman <michael@snoyman.com>
+maintainer:      Michael Snoyman <michael@snoyman.com>
+synopsis:        Error handling using extensible exceptions outside the IO monad.
+description:     Defines a data type, Attempt, which has a Success and Failure constructor. Failure contains an extensible exception.
+category:        Data
+stability:       unstable
+cabal-version:   >= 1.2
+build-type:      Simple
+homepage:        http://github.com/snoyberg/attempt/tree/master
+
+library
+    build-depends:   base >= 4 && < 5,
+                     syb,
+                     transformers >= 0.1.4.0
+    exposed-modules: Data.Attempt
+                     Data.Attempt.Helper
+                     Control.Monad.Attempt
+                     Control.Monad.Attempt.Class
+    ghc-options:     -Wall
