foldl-exceptions (empty) → 1.0.0.0
raw patch · 5 files changed
+269/−0 lines, 5 filesdep +basedep +doctestdep +foldl
Dependencies added: base, doctest, foldl, safe-exceptions
Files
- changelog.md +3/−0
- foldl-exceptions.cabal +46/−0
- library/Control/Foldl/Exceptions.hs +199/−0
- license.txt +18/−0
- test/doctest.hs +3/−0
+ changelog.md view
@@ -0,0 +1,3 @@+1.0.0.0++ * Initial release
+ foldl-exceptions.cabal view
@@ -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
+ library/Control/Foldl/Exceptions.hs view
@@ -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)
+ license.txt view
@@ -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.
+ test/doctest.hs view
@@ -0,0 +1,3 @@+import Test.DocTest++main = doctest ["library/Control/Foldl/Exceptions.hs"]