packages feed

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 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++[![Hackage version](https://img.shields.io/hackage/v/multi-except?color=purple)](https://hackage.haskell.org/package/multi-except)+[![CI Status](https://img.shields.io/github/actions/workflow/status/414owen/multi-except/haskell-ci.yml)](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