diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,3 @@
+1.0.0.0
+
+  * Initial release
diff --git a/foldl-exceptions.cabal b/foldl-exceptions.cabal
new file mode 100644
--- /dev/null
+++ b/foldl-exceptions.cabal
@@ -0,0 +1,46 @@
+cabal-version: 2.0
+
+name: foldl-exceptions
+version: 1.0.0.0
+
+synopsis: Exception handling with FoldM
+category: Web
+
+description:
+    Adds support for tuning exception handling behavior when
+    using @FoldM@ from the @foldl@ package.
+
+homepage:    https://github.com/typeclasses/foldl-exceptions
+bug-reports: https://github.com/typeclasses/foldl-exceptions/issues
+
+author:     Chris Martin
+maintainer: Chris Martin, Julie Moronuki
+
+copyright: 2018 Typeclass Consulting, LLC
+license: MIT
+license-file: license.txt
+
+build-type: Simple
+tested-with: GHC==8.6.1
+
+extra-source-files:
+    changelog.md
+
+library
+    hs-source-dirs: library
+    default-language: Haskell2010
+
+    exposed-modules:
+        Control.Foldl.Exceptions
+
+    build-depends:
+        base >=4.10 && <5
+      , foldl
+      , safe-exceptions
+
+test-suite doctest
+    type: exitcode-stdio-1.0
+    hs-source-dirs: test
+    default-language: Haskell2010
+    main-is: doctest.hs
+    build-depends: base, doctest
diff --git a/library/Control/Foldl/Exceptions.hs b/library/Control/Foldl/Exceptions.hs
new file mode 100644
--- /dev/null
+++ b/library/Control/Foldl/Exceptions.hs
@@ -0,0 +1,199 @@
+{-# OPTIONS_GHC -Wall #-}
+
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+{- |
+
+When 'IO' is the monad of a monadic fold, each step in the evaluation of the
+fold might throw an exception, thus causing the entire evaluation to fail. This
+module provides some functions for capturing the results from /part of/ a fold
+even if some of the steps did not succeed.
+
+This module uses 'try' from the @safe-exceptions@ package and will not catch
+async exceptions.
+
+-}
+
+module Control.Foldl.Exceptions
+  (
+  -- * Stopping at the first exception
+    exHalt_, exHalt
+
+  -- * Continuing past failed steps
+  , exSkip_, exSkip
+
+  ) where
+
+-- foldl
+import Control.Foldl
+
+-- safe-exceptions
+import Control.Exception.Safe (Exception, MonadCatch, SomeException, try)
+
+{- |
+
+Performs the steps of a fold up until the first step that throws an exception,
+then produces the result obtained thus far.
+
+==== Example
+
+>>> import Control.Exception
+>>> f x = if x < 10 then return x else throw Overflow
+>>> xs = [1, 2, 500, 4] :: [Integer]
+
+Since @f 500@ produces an exception, the following fold fails:
+
+>>> fold1 = premapM f (generalize list)
+>>> foldM fold1 xs
+*** Exception: arithmetic overflow
+
+By applying 'untilFirstException', we can produce a new fold that returns
+the intermediate result at the point where the exception occurs.
+
+>>> fold2 = exHalt_ fold1
+>>> foldM fold2 xs
+[1,2]
+
+-}
+
+exHalt_ :: forall m a b.
+    (Monad m, MonadCatch m) => FoldM m a b -> FoldM m a b
+
+exHalt_ f = fmap snd (exHalt @SomeException f)
+
+{- |
+
+Performs the steps of a fold up until the first step that throws an exception,
+then produces a tuple containing:
+
+  1. The exception that was thrown, if any.
+  2. The result obtained thus far.
+
+The first type parameter lets you specify what type of exception to catch. Any
+other type of exception will still cause the entire fold's evaluation to fail.
+
+==== Example
+
+>>> import Control.Exception
+>>> f x = if x < 10 then return x else throw Overflow
+>>> xs = [1, 2, 500, 4] :: [Integer]
+>>> fold1 = premapM f (generalize list)
+
+>>> :set -XTypeApplications
+>>> fold2 = exHalt @ArithException fold1
+
+>>> foldM fold2 xs
+(Just arithmetic overflow,[1,2])
+
+-}
+
+exHalt :: forall e m a b.
+    (Exception e, Monad m, MonadCatch m) =>
+    FoldM m a b -> FoldM m a (Maybe e, b)
+
+exHalt (FoldM step begin done) = FoldM step' begin' done'
+  where
+    begin' =
+      do
+        y <- begin
+        return (Nothing, y)
+
+    step' x'@(Just _, _) _ = return x'
+    step' (Nothing, x1) a =
+      do
+        x2Either <- try (step x1 a)
+        case x2Either of
+            Left e   -> return (Just e, x1)
+            Right x2 -> return (Nothing, x2)
+
+    done' (eMaybe, x) =
+      do
+        b <- done x
+        return (eMaybe, b)
+
+{- |
+
+Perform only steps of a fold that succeed, ignoring any steps that fail.
+
+==== Example
+
+>>> import Control.Exception
+>>> f x = if x < 10 then return x else throw Overflow
+>>> xs = [1, 2, 500, 4] :: [Integer]
+
+Since @f 500@ produces an exception, the following fold fails:
+
+>>> fold1 = premapM f (generalize list)
+>>> foldM fold1 xs
+*** Exception: arithmetic overflow
+
+By applying 'exSkip_', we can produce a new fold that produces a result from all
+steps that /don't/ fail:
+
+>>> fold2 = exSkip_ fold1
+>>> foldM fold2 xs
+[1,2,4]
+
+-}
+
+exSkip_ :: forall m a b.
+    (Monad m, MonadCatch m) => FoldM m a b -> FoldM m a b
+
+exSkip_ f = fmap snd (exSkip @SomeException f)
+
+{- |
+
+Perform only steps of a fold that succeed, collecting the exceptions thrown from
+each step that fail. Produces a tuple containing:
+
+  1. A list of any exceptions thrown.
+  2. The result obtained from the steps that succeeded.
+
+The first type parameter lets you specify what type of exception to catch. Any
+other type of exception will still cause the entire fold's evaluation to fail.
+
+==== Example
+
+>>> import Control.Exception
+>>> f x = if x < 10 then return x else throw Overflow
+>>> xs = [1, 2, 500, 4] :: [Integer]
+
+Since @f 500@ produces an exception, the following fold fails:
+
+>>> fold1 = premapM f (generalize list)
+>>> foldM fold1 xs
+*** Exception: arithmetic overflow
+
+By applying 'exSkip', we can produce a new fold that produces a result from all
+steps that /don't/ fail:
+
+>>> :set -XTypeApplications
+>>> fold2 = exSkip @ArithException fold1
+>>> foldM fold2 xs
+([arithmetic overflow],[1,2,4])
+
+-}
+
+exSkip :: forall e m a b.
+    (Exception e, Monad m, MonadCatch m) =>
+    FoldM m a b -> FoldM m a ([e], b)
+
+exSkip (FoldM step begin done) = FoldM step' begin' done'
+  where
+    begin' =
+      do
+        y <- begin
+        return (id, y)
+
+    step' (es, x1) a =
+      do
+        x2Either <- try (step x1 a)
+        case x2Either of
+            Left e   -> return (es . (e :), x1)
+            Right x2 -> return (es, x2)
+
+    done' (es, x) =
+      do
+        b <- done x
+        return (es [], b)
diff --git a/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,18 @@
+Copyright 2019 Typeclass Consulting, LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/test/doctest.hs b/test/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest.hs
@@ -0,0 +1,3 @@
+import Test.DocTest
+
+main = doctest ["library/Control/Foldl/Exceptions.hs"]
