multi-except 0.3.0.0 → 1.0.0
raw patch · 19 files changed
+444/−246 lines, 19 filesdep −dlist-nonemptydep ~hspecdep ~semigroupoidsPVP ok
version bump matches the API change (PVP)
Dependencies removed: dlist-nonempty
Dependency ranges changed: hspec, semigroupoids
API changes (from Hackage documentation)
- Control.Applicative.MultiExcept: instance (GHC.Read.Read a, GHC.Read.Read err) => GHC.Read.Read (Control.Applicative.MultiExcept.MultiExcept err a)
- Control.Applicative.MultiExcept: instance Data.Functor.Alt.Alt (Control.Applicative.MultiExcept.MultiExcept err)
+ Control.Applicative.MultiExcept: instance GHC.Base.Functor Control.Applicative.MultiExcept.NonEmptyDList
+ Control.Applicative.MultiExcept: instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Applicative.MultiExcept.NonEmptyDList a)
+ Control.Applicative.MultiExcept: instance GHC.Classes.Ord a => GHC.Classes.Ord (Control.Applicative.MultiExcept.NonEmptyDList a)
+ Control.Applicative.MultiExcept: instance GHC.Show.Show a => GHC.Show.Show (Control.Applicative.MultiExcept.NonEmptyDList a)
+ Control.Applicative.MultiExcept: mapMultiExcept :: (err -> err') -> (a -> a') -> MultiExcept err a -> MultiExcept err' a'
+ Control.Applicative.MultiExcept: or :: MultiExcept err a -> MultiExcept err a -> MultiExcept err a
- Control.Applicative.MultiExcept: fromEitherPoly :: Either (NonEmptyDList err) a -> MultiExcept err a
+ Control.Applicative.MultiExcept: fromEitherPoly :: Either (NonEmpty err) a -> MultiExcept err a
- Control.Applicative.MultiExcept: throwErrors :: forall a err. NonEmptyDList err -> MultiExcept err a
+ Control.Applicative.MultiExcept: throwErrors :: forall a err. NonEmpty err -> MultiExcept err a
Files
- CHANGELOG.md +6/−0
- Control/Applicative/MultiExcept.hs +0/−94
- README.md +45/−0
- multi-except-semigroupoids/Control/Applicative/MultiExcept/Alt.hs +22/−0
- multi-except.cabal +54/−16
- multi-except/Control/Applicative/MultiExcept.hs +175/−0
- test/Main.hs +12/−12
- test/Test/Alt.hs +0/−22
- test/Test/Applicative.hs +0/−28
- test/Test/Bifunctor.hs +0/−18
- test/Test/Foldable.hs +0/−17
- test/Test/Functor.hs +0/−20
- test/Test/MultiExcept/Alt.hs +22/−0
- test/Test/MultiExcept/Applicative.hs +27/−0
- test/Test/MultiExcept/Bifunctor.hs +28/−0
- test/Test/MultiExcept/Foldable.hs +15/−0
- test/Test/MultiExcept/Functor.hs +20/−0
- test/Test/MultiExcept/Traversable.hs +18/−0
- test/Test/Traversable.hs +0/−19
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for multi-except +## 1.0.0 -- 2024-01-03++* Split into two libraries, `multi-except`, and `multi-except:semigroupoid-instances`+* Migrated to custom dlist, removing the dependency+* Support more GHC versions (tested down to 7.0.4)+ ## 0.3.0.0 -- 2021-06-06 * Switch to dlist-nonempty package
− Control/Applicative/MultiExcept.hs
@@ -1,94 +0,0 @@-{-|-Module : Control.Applicative.MultiExcept-Copyright : (c) Owen Shepherd, 2021-License : MIT-Maintainer : owen@owen.cafe-Stability : stable-Portability : portable--}--{-# LANGUAGE ScopedTypeVariables #-}--module Control.Applicative.MultiExcept- ( MultiExcept- , fromEither- , fromEitherPoly- , join- , runMultiExcept- , succeed- , throwError- , throwErrors- ) where--import Data.Bifunctor-import Data.Functor.Alt-import Data.DList.NonEmpty (NonEmptyDList)---- | A 'MultiExcept' is a success value, or one or more errors.-data MultiExcept err a- = Success a- | Errors (NonEmptyDList err)- deriving (Eq, Ord, Read, Show)---- | Run the computation.-runMultiExcept :: MultiExcept err a -> Either (NonEmptyDList err) a-runMultiExcept (Errors errs) = Left errs-runMultiExcept (Success a) = Right a---- | Throw a single error.-throwError :: forall a err. err -> MultiExcept err a-throwError = Errors . pure---- | Throw one or more errors.-throwErrors :: forall a err. NonEmptyDList err -> MultiExcept err a-throwErrors = Errors---- | Embeds a value into a 'MultiExcept' context.-succeed :: forall err a. a -> MultiExcept err a-succeed = Success---- | Convert an 'Either' to a 'MultiExcept'.-fromEither :: Either err a -> MultiExcept err a-fromEither (Left err) = throwError err-fromEither (Right a) = Success a---- | Convert a multi-error 'Either' to a 'MultiExcept'.-fromEitherPoly :: Either (NonEmptyDList err) a -> MultiExcept err a-fromEitherPoly (Left errs) = Errors errs-fromEitherPoly (Right a) = Success a---- | Join nested 'MultiExcept's with the same error type.--- Note that this doesn't imply a __useful__ 'Monad' instance.--- The instance defined in terms of join discards errors on the RHS of '>>='.-join :: MultiExcept err (MultiExcept err a) -> MultiExcept err a-join (Success a) = a-join (Errors a) = Errors a--instance Functor (MultiExcept err) where- fmap f (Success a) = Success $ f a- fmap _ (Errors errs) = Errors errs--instance Bifunctor MultiExcept where- bimap _ fa (Success a) = Success $ fa a- bimap ferr _ (Errors err) = Errors $ fmap ferr err--instance Applicative (MultiExcept err) where- pure = Success-- Errors l <*> Errors l' = Errors $ l <> l'- Success f <*> Success a = Success $ f a- Errors l <*> _ = Errors l- _ <*> Errors l = Errors l--instance Alt (MultiExcept err) where- Success a <!> _ = Success a- _ <!> Success a = Success a- Errors l <!> Errors r = Errors (l <> r)--instance Foldable (MultiExcept err) where- foldr f acc (Success a) = f a acc- foldr _ acc _ = acc--instance Traversable (MultiExcept err) where- traverse f (Success a) = Success <$> f a- traverse _ (Errors err) = pure $ Errors err
+ README.md view
@@ -0,0 +1,45 @@+# multi-except++[](https://hackage.haskell.org/package/multi-except)+[](https://github.com/414owen/multi-except/actions)++multi-except - succeed, or return one or more errors++## Adding the dependency++```+-- in your cabal file+ -- Add the main package (only depends on base!)+ , multi-except+ -- For the Alt instance (depends on semigroupoids)+ , multi-except:semigroupoid-instances+```++## Usage+++```haskell+{-# LANGUAGE ApplicativeDo #-}++import Control.Applicative.MultiExcept++errors :: MultiExcept String (Int, Int, Int)+errors = do+ a <- throwError "no monad instance"+ b <- pure 12+ c <- throwError "i am scared"+ pure (a, b, c)++-- errors: Errors ["no monad instance", "i am scared"]+```++The use of `ApplicativeDo` is significant and necessary for using+`MultiExcept` with do notation.++`MultiExcept` is not a `Monad`, only an `Applicative`, so a few constraints+apply, such as not being able to determine the structure of the rest of the+computation based on a previously do-bound value. If the previous sentence was+confusing, then you might want to consider using a writer monad instead.++To compose with other applicative effects, you can use+[`Data.Functor.Compose`](https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Functor-Compose.html).
+ multi-except-semigroupoids/Control/Applicative/MultiExcept/Alt.hs view
@@ -0,0 +1,22 @@+{-|+Module : Control.Applicative.MultiExcept.Alt+Copyright : (c) Owen Shepherd, 2023+License : MIT+Maintainer : owen@owen.cafe+Stability : stable+Portability : portable+-}++{-# LANGUAGE CPP #-}+#if MIN_VERSION_base(4,9,0)+{-# OPTIONS_GHC -Wno-orphans #-}+#endif++module Control.Applicative.MultiExcept.Alt where++import Prelude ()+import Control.Applicative.MultiExcept+import Data.Functor.Alt (Alt(..))++instance Alt (MultiExcept err) where+ (<!>) = or
multi-except.cabal view
@@ -1,37 +1,75 @@-cabal-version: >=1.10+cabal-version: 3.0 name: multi-except-version: 0.3.0.0+version: 1.0.0 synopsis: Multiple Exceptions-description: Exception type that supports reporting multiple exceptions+description: Report multiple errors, or a single correct value. license: MIT license-file: LICENSE author: Owen Shepherd maintainer: owen@owen.cafe category: Exceptions build-type: Simple-extra-source-files: CHANGELOG.md+extra-doc-files: CHANGELOG.md+ , README.md homepage: https://github.com/414owen/multi-except +tested-with:+ GHC==9.6.2+ , GHC==9.4.5+ , GHC==9.2.8+ , GHC==9.0.2+ , GHC==8.10.7+ , GHC==8.8.4+ , GHC==8.6.5+ , GHC==8.4.4+ , GHC==8.2.2+ , GHC==8.0.2+ , GHC==7.10.3+ , GHC==7.8.4+ , GHC==7.6.3+ , GHC==7.4.2+ , GHC==7.2.2+ , GHC==7.0.4++source-repository head+ type: git+ location: https://github.com/414owen/multi-except.git++common common-import+ default-language: Haskell2010+ ghc-options: -Wall -W+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat+ library+ import: common-import exposed-modules: Control.Applicative.MultiExcept+ hs-source-dirs: multi-except build-depends: base >=4.0 && <5- , dlist-nonempty- , semigroupoids- default-language: Haskell2010 +library semigroupoid-instances+ import: common-import+ exposed-modules: Control.Applicative.MultiExcept.Alt+ hs-source-dirs: multi-except-semigroupoids+ visibility: public+ build-depends: base >=4.0 && <5+ , multi-except+ , semigroupoids >=1 && <7+ test-suite unit-tests+ import: common-import type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Main.hs- default-language: Haskell2010- other-modules: Test.Functor- , Test.Applicative- , Test.Alt- , Test.Bifunctor- , Test.Foldable- , Test.Traversable+ other-modules: Test.MultiExcept.Functor+ , Test.MultiExcept.Applicative+ , Test.MultiExcept.Alt+ , Test.MultiExcept.Bifunctor+ , Test.MultiExcept.Foldable+ , Test.MultiExcept.Traversable build-depends: base , multi-except- , semigroupoids- , hspec+ , multi-except:semigroupoid-instances+ , hspec >=2 && <3+ , semigroupoids >=1 && <7
+ multi-except/Control/Applicative/MultiExcept.hs view
@@ -0,0 +1,175 @@+{-|+Module : Control.Applicative.MultiExcept+Copyright : (c) Owen Shepherd, 2021+License : MIT+Maintainer : owen@owen.cafe+Stability : stable+Portability : portable+-}++{-# LANGUAGE CPP #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Control.Applicative.MultiExcept+ ( MultiExcept+ , fromEither+ , fromEitherPoly+ , join+ , or+ , runMultiExcept+ , succeed+ , throwError+ , throwErrors+ , mapMultiExcept+ ) where++import Prelude (Eq(..), Ord(..), Either(..), (.), ($), id, Show(..), (++))++import Control.Applicative (Applicative(..))+#if MIN_VERSION_base(4,8,0)+import Data.Bifunctor+#endif+import Data.Functor (Functor(..), (<$>))+import Data.Foldable (Foldable(..))+import Data.Traversable (Traversable(..))+#if MIN_VERSION_base(4,9,0)+import Data.List.NonEmpty (NonEmpty(..))+#endif+++-- NonEmptyDList++-- | This is written here because:+-- * The version in dlist is currently limited to ghc>=8.0+-- * The version in dlist-nonempty is too heavy on dependencies+-- * We only need a few trivial features anyway++data NonEmptyDList a = NonEmptyDList !a !([a] -> [a])++{-# INLINE nedlSingleton #-}+nedlSingleton :: a -> NonEmptyDList a+nedlSingleton a = NonEmptyDList a id++{-# INLINE runNonEmptyDList #-}+runNonEmptyDList :: NonEmptyDList a -> (a, [a])+runNonEmptyDList (NonEmptyDList x xs) = (x, xs [])++instance Eq a => Eq (NonEmptyDList a) where+ NonEmptyDList x xs == NonEmptyDList y ys = x : xs [] == y : ys []++instance Ord a => Ord (NonEmptyDList a) where+ NonEmptyDList x xs `compare` NonEmptyDList y ys = (x : xs []) `compare` (y : ys [])++instance Show a => Show (NonEmptyDList a) where+ show nedl = case runNonEmptyDList nedl of+ (x, xs) -> show $ x : xs++appendNedl :: NonEmptyDList a -> NonEmptyDList a -> NonEmptyDList a+appendNedl (NonEmptyDList x xs) (NonEmptyDList y ys) = NonEmptyDList x $ xs . (y:) . ys++-- WARNING: O(n) space+-- TODO Make this constant space+instance Functor NonEmptyDList where+ fmap f nedl = case runNonEmptyDList nedl of+ (x, xs) -> NonEmptyDList (f x) (fmap f xs ++)++-- | A 'MultiExcept' is a success value, or one or more errors.+data MultiExcept err a+ = Success !a+ | Errors !(NonEmptyDList err)+ deriving (Eq, Ord, Show)++-- | Run the computation.+runMultiExcept :: MultiExcept err a -> Either (NonEmptyDList err) a+runMultiExcept (Errors errs) = Left errs+runMultiExcept (Success a) = Right a++-- | Throw a single error.+throwError :: forall a err. err -> MultiExcept err a+throwError = Errors . nedlSingleton++#if MIN_VERSION_base(4,9,0)++-- | Throw one or more errors.+throwErrors :: forall a err. NonEmpty err -> MultiExcept err a+throwErrors (err :| errs) = Errors $ NonEmptyDList err (errs ++)++#else++-- | Throw one or more errors.+throwErrors :: forall a err. (err, [err]) -> MultiExcept err a+throwErrors (err, errs) = Errors $ NonEmptyDList err (errs ++)++#endif++-- | Embeds a value into a 'MultiExcept' context.+succeed :: forall err a. a -> MultiExcept err a+succeed = Success++-- | Convert an 'Either' to a 'MultiExcept'.+fromEither :: Either err a -> MultiExcept err a+fromEither (Left err) = throwError err+fromEither (Right a) = Success a++#if MIN_VERSION_base(4,9,0)++-- | Convert a multi-error 'Either' to a 'MultiExcept'.+fromEitherPoly :: Either (NonEmpty err) a -> MultiExcept err a+fromEitherPoly (Left errs) = throwErrors errs+fromEitherPoly (Right a) = Success a++#else++-- | Convert a multi-error 'Either' to a 'MultiExcept'.+fromEitherPoly :: Either (err, [err]) a -> MultiExcept err a+fromEitherPoly (Left errs) = throwErrors errs+fromEitherPoly (Right a) = Success a++#endif++-- | Join nested 'MultiExcept's with the same error type.+-- Note that this doesn't imply a __useful__ 'Monad' instance.+-- The instance defined in terms of join discards errors on the RHS of '>>='.+join :: MultiExcept err (MultiExcept err a) -> MultiExcept err a+join (Success a) = a+join (Errors a) = Errors a++instance Functor (MultiExcept err) where+ fmap f (Success a) = Success $ f a+ fmap _ (Errors errs) = Errors errs++mapMultiExcept:: (err -> err') -> (a -> a') -> MultiExcept err a -> MultiExcept err' a'+mapMultiExcept _ fa (Success a) = Success $ fa a+mapMultiExcept ferr _ (Errors err) = Errors $ fmap ferr err+++#if MIN_VERSION_base(4,8,0)++-- | WARNING: O(n) space and time in the length of the amount of errors+-- this could be fixed by changing the difference list Functor instance.+instance Bifunctor MultiExcept where+ bimap = mapMultiExcept++#endif++instance Applicative (MultiExcept err) where+ pure = Success++ Errors l <*> Errors l' = Errors $ appendNedl l l'+ Success f <*> Success a = Success $ f a+ Errors l <*> _ = Errors l+ _ <*> Errors l = Errors l++or :: MultiExcept err a -> MultiExcept err a -> MultiExcept err a+Success a `or` _ = Success a+_ `or` Success a = Success a+Errors l `or` Errors r = Errors $ appendNedl l r++instance Foldable (MultiExcept err) where+ foldr f acc (Success a) = f a acc+ foldr _ acc _ = acc++instance Traversable (MultiExcept err) where+ traverse f (Success a) = Success <$> f a+ traverse _ (Errors err) = pure $ Errors err
test/Main.hs view
@@ -2,21 +2,21 @@ import Test.Hspec -import qualified Test.Applicative-import qualified Test.Alt-import qualified Test.Functor-import qualified Test.Bifunctor-import qualified Test.Foldable-import qualified Test.Traversable+import qualified Test.MultiExcept.Applicative+import qualified Test.MultiExcept.Alt+import qualified Test.MultiExcept.Functor+import qualified Test.MultiExcept.Bifunctor+import qualified Test.MultiExcept.Foldable+import qualified Test.MultiExcept.Traversable main :: IO () main = hspec spec spec :: Spec spec = do- Test.Functor.spec- Test.Applicative.spec- Test.Alt.spec- Test.Bifunctor.spec- Test.Foldable.spec- Test.Traversable.spec+ Test.MultiExcept.Functor.spec+ Test.MultiExcept.Applicative.spec+ Test.MultiExcept.Alt.spec+ Test.MultiExcept.Bifunctor.spec+ Test.MultiExcept.Foldable.spec+ Test.MultiExcept.Traversable.spec
− test/Test/Alt.hs
@@ -1,22 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}--module Test.Alt- ( spec- ) where--import Test.Hspec--import Data.Functor.Alt-import Control.Applicative.MultiExcept--spec :: Spec-spec = describe "Alt instance" $ do- it "propagates left succeed" $- succeed 1 <!> throwError 3 `shouldBe` succeed 1- it "propagates right succeed" $- throwError () <!> succeed 1 `shouldBe` succeed 1- it "prioritizes left succeed" $- succeed 1 <!> succeed 2 `shouldBe` succeed @() 1- it "propagates both errors" $ do- throwError 1 <!> throwError 2`shouldBe` throwErrors @() [1, 2]
− test/Test/Applicative.hs
@@ -1,28 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TupleSections #-}--module Test.Applicative- ( spec- ) where--import Test.Hspec--import Control.Applicative.MultiExcept--testErrors :: MultiExcept Int Int-testErrors = fromEitherPoly $ Left [2, 3]--spec :: Spec-spec = describe "Applicative instance" $ do- describe "pure" $- it "succeeds" $- pure 5 `shouldBe` (succeed 5 :: MultiExcept () Int)- describe "<*>" $ do- it "accumulates errors" $- throwError 2 <*> throwError 3 `shouldBe` testErrors- it "propagates successes" $- pure (3,) <*> pure 4 `shouldBe` succeed @() (3, 4)- it "errors when only one side is successful" $ do- succeed (+ 1) <*> testErrors `shouldBe` testErrors- throwError 2 <*> succeed () `shouldBe` throwError @Int 2
− test/Test/Bifunctor.hs
@@ -1,18 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}--module Test.Bifunctor- ( spec- ) where--import Test.Hspec--import Data.Bifunctor-import Control.Applicative.MultiExcept--spec :: Spec-spec = describe "Bifunctor instance" $ do- it "maps errors with first" $- first (+ 1) (throwErrors [3, 4]) `shouldBe` throwErrors @() [4, 5]- it "maps successes with second" $- second (+ 1) (succeed 3) `shouldBe` succeed @() 4
− test/Test/Foldable.hs
@@ -1,17 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}--module Test.Foldable- ( spec- ) where--import Test.Hspec--import Control.Applicative.MultiExcept--spec :: Spec-spec = describe "Foldable instance" $ do- it "f is called for success" $- foldr (+) 2 (succeed 1)`shouldBe` 3- it "acc is returned for errors" $- foldr (+) 2 (throwError 1)`shouldBe` 2
− test/Test/Functor.hs
@@ -1,20 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}--module Test.Functor- ( spec- ) where--import Test.Hspec--import Control.Applicative.MultiExcept--testErrors :: MultiExcept Int Int-testErrors = fromEitherPoly (Left [2, 3])--spec :: Spec-spec = describe "Functor instance" $ do- it "transforms success value" $- ((+ 3) <$> succeed 2) `shouldBe` succeed @() 5- it "doesn't affect errors" $ do- fmap (+ 3) testErrors `shouldBe` testErrors
+ test/Test/MultiExcept/Alt.hs view
@@ -0,0 +1,22 @@+module Test.MultiExcept.Alt+ ( spec+ ) where++import Test.Hspec++import Prelude (Int, ($))+import Control.Applicative (Applicative(..))+import Data.Functor.Alt+import Control.Applicative.MultiExcept+import Control.Applicative.MultiExcept.Alt ()++spec :: Spec+spec = describe "Alt instance" $ do+ it "propagates left succeed" $ do+ succeed 1 <!> throwError () `shouldBe` succeed (1 :: Int)+ it "propagates right succeed" $+ throwError () <!> succeed 1 `shouldBe` succeed (1 :: Int)+ it "prioritizes left succeed" $+ succeed 1 <!> succeed 2 `shouldBe` (succeed 1 :: MultiExcept () Int)+ it "propagates both errors" $ do+ throwError 1 <!> throwError 2 `shouldBe` (throwError 1 *> throwError 2 :: MultiExcept Int ())
+ test/Test/MultiExcept/Applicative.hs view
@@ -0,0 +1,27 @@+module Test.MultiExcept.Applicative+ ( spec+ ) where++import Test.Hspec++import Prelude (Int, ($), Num(..))++import Control.Applicative (Applicative(..))+import Control.Applicative.MultiExcept++testErrors :: MultiExcept Int Int+testErrors = throwError 2 *> throwError 3++spec :: Spec+spec = describe "Applicative instance" $ do+ describe "pure" $+ it "succeeds" $+ pure 5 `shouldBe` (succeed 5 :: MultiExcept () Int)+ describe "<*>" $ do+ it "accumulates errors" $+ throwError 2 <*> throwError 3 `shouldBe` testErrors+ it "propagates successes" $+ pure (3,4) `shouldBe` (succeed (3, 4) :: MultiExcept () (Int, Int))+ it "errors when only one side is successful" $ do+ succeed (+ 1) <*> testErrors `shouldBe` testErrors+ throwError 2 <*> succeed () `shouldBe` (throwError 2 :: MultiExcept Int Int)
+ test/Test/MultiExcept/Bifunctor.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE CPP #-}++module Test.MultiExcept.Bifunctor+ ( spec+ ) where++import Test.Hspec++#if MIN_VERSION_base(4,8,0)++import Data.Bifunctor+import Control.Applicative.MultiExcept++spec :: Spec+spec = describe "Bifunctor instance" $ do+ it "maps errors with first" $+ first (+ 1) (throwError 3 *> throwError 4) `shouldBe` (throwError 4 *> throwError 5 :: MultiExcept Int ())+ it "maps successes with second" $+ second (+ 1) (succeed 3) `shouldBe` (succeed 4 :: MultiExcept Int Int)++#else++import Control.Applicative++spec :: Spec+spec = pure ()++#endif
+ test/Test/MultiExcept/Foldable.hs view
@@ -0,0 +1,15 @@+module Test.MultiExcept.Foldable+ ( spec+ ) where++import Test.Hspec++import qualified Data.Foldable as Foldable+import Control.Applicative.MultiExcept++spec :: Spec+spec = describe "Foldable instance" $ do+ it "f is called for success" $+ Foldable.foldr (+) 2 (succeed 1) `shouldBe` (3 :: Int)+ it "acc is returned for errors" $+ Foldable.foldr (+) 2 (throwError (1 :: Int)) `shouldBe` (2 :: Int)
+ test/Test/MultiExcept/Functor.hs view
@@ -0,0 +1,20 @@+module Test.MultiExcept.Functor+ ( spec+ ) where++import Test.Hspec++import Prelude (Int, ($), Num(..))+import Data.Functor ((<$>), fmap)+import Control.Applicative (Applicative(..))+import Control.Applicative.MultiExcept++testErrors :: MultiExcept Int Int+testErrors = throwError 2 *> throwError 3++spec :: Spec+spec = describe "Functor instance" $ do+ it "transforms success value" $+ ((+ 3) <$> succeed 2) `shouldBe` (succeed 5 :: MultiExcept () Int)+ it "doesn't affect errors" $ do+ fmap (+ 3) testErrors `shouldBe` testErrors
+ test/Test/MultiExcept/Traversable.hs view
@@ -0,0 +1,18 @@+module Test.MultiExcept.Traversable+ ( spec+ ) where++import Prelude (Int, Maybe(..), ($), const)++import Data.Traversable+import Test.Hspec+import Control.Applicative.MultiExcept++spec :: Spec+spec = describe "Traversable instance" $ do+ it "Just works™" $+ traverse Just (succeed 3) `shouldBe` Just (succeed 3 :: MultiExcept () Int)+ it "leaves errors" $+ traverse Just (throwError 3) `shouldBe` Just (throwError 3 :: MultiExcept Int ())+ it "Nothing works™" $+ traverse (const Nothing :: a -> Maybe a) (succeed 3 :: MultiExcept () Int) `shouldBe` Nothing
− test/Test/Traversable.hs
@@ -1,19 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE TypeApplications #-}--module Test.Traversable- ( spec- ) where--import Test.Hspec--import Control.Applicative.MultiExcept--spec :: Spec-spec = describe "Traversable instance" $ do- it "Just works™" $- traverse Just (succeed 3) `shouldBe` Just (succeed @() 3)- it "leaves errors" $- traverse Just (throwError 3) `shouldBe` Just (throwError @() 3)- it "Nothing works™" $- traverse (const Nothing :: a -> Maybe a) (succeed @() 3) `shouldBe` Nothing