applicative-fail 0.0.3 → 1.0.0
raw patch · 6 files changed
+508/−92 lines, 6 filesdep +QuickCheckdep +applicative-faildep +checkersdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, applicative-fail, checkers, dlist, mtl, tasty, tasty-quickcheck, transformers, transformers-base
Dependency ranges changed: base
API changes (from Hackage documentation)
- Control.Applicative.Fail: bindFail :: Monoid e => Fail e a -> (a -> Fail e b) -> Fail e b
- Control.Applicative.Fail: composeFail :: Monoid e => (a -> Fail e b) -> (b -> Fail e c) -> a -> Fail e c
- Control.Applicative.Fail: ffail :: e -> Fail [e] a
- Control.Applicative.Fail: fsucc :: a -> Fail e a
- Control.Applicative.Fail: fwarn :: e -> a -> Fail [e] a
+ Control.Applicative.Fail: afail :: Applicative f => e -> Fail (f e) a
+ Control.Applicative.Fail: awarn :: Applicative f => e -> a -> Fail (f e) a
+ Control.Applicative.Fail: fNull :: (Eq e, Monoid e) => Fail e a -> Bool
+ Control.Applicative.Fail: runDLFail :: Fail (DList e) a -> ([e], Maybe a)
+ Control.Applicative.Fail: runFail :: Monoid e => Fail e a -> (e, Maybe a)
+ Control.Monad.Fail: FailT :: m (Fail e a) -> FailT e m a
+ Control.Monad.Fail: instance (Applicative m, Monoid a, Monoid e) => Monoid (FailT e m a)
+ Control.Monad.Fail: instance (Monad m, Monoid e) => MonadError e (FailT e m)
+ Control.Monad.Fail: instance (MonadReader r m, Monoid e) => MonadReader r (FailT e m)
+ Control.Monad.Fail: instance (MonadState s m, Monoid e) => MonadState s (FailT e m)
+ Control.Monad.Fail: instance (MonadWriter w m, Monoid e) => MonadWriter w (FailT e m)
+ Control.Monad.Fail: instance (Monoid e, Functor m, Monad m) => Applicative (FailT e m)
+ Control.Monad.Fail: instance (Monoid e, Monad m) => Monad (FailT e m)
+ Control.Monad.Fail: instance (Monoid e, MonadBase b m) => MonadBase b (FailT e m)
+ Control.Monad.Fail: instance Constructor C1_0FailT
+ Control.Monad.Fail: instance Datatype D1FailT
+ Control.Monad.Fail: instance Eq (m (Fail e a)) => Eq (FailT e m a)
+ Control.Monad.Fail: instance Foldable m => Foldable (FailT e m)
+ Control.Monad.Fail: instance Functor m => Functor (FailT e m)
+ Control.Monad.Fail: instance Generic (FailT e m a)
+ Control.Monad.Fail: instance MonadTrans (FailT e)
+ Control.Monad.Fail: instance Ord (m (Fail e a)) => Ord (FailT e m a)
+ Control.Monad.Fail: instance Selector S1_0_0FailT
+ Control.Monad.Fail: instance Show (m (Fail e a)) => Show (FailT e m a)
+ Control.Monad.Fail: instance Traversable m => Traversable (FailT e m)
+ Control.Monad.Fail: instance Typeable FailT
+ Control.Monad.Fail: mfail :: (Applicative f, Applicative m) => e -> FailT (f e) m a
+ Control.Monad.Fail: mwarn :: (Applicative f, Applicative m) => e -> FailT (f e) m ()
+ Control.Monad.Fail: newtype FailT e m a
+ Control.Monad.Fail: runFailC :: FailT e m a -> Compose m (Fail e) a
+ Control.Monad.Fail: runFailI :: FailT e Identity a -> Fail e a
+ Control.Monad.Fail: runFailT :: FailT e m a -> m (Fail e a)
Files
- CHANGELOG.md +27/−0
- README.md +39/−32
- applicative-fail.cabal +44/−5
- src/Control/Applicative/Fail.hs +90/−55
- src/Control/Monad/Fail.hs +221/−0
- test/Test.hs +87/−0
+ CHANGELOG.md view
@@ -0,0 +1,27 @@+# CHANGELOG++## 1.0.0++### Added++* Module `Control.Monad.Fail` containing monadic version of+ `Control.Applicative.Fail`+* Tests added: all properties (Monad, Applicative, Monoid laws) are+ prooved with QuickCheck and package `checkers`+* `runFail` and `runDLFail`: functions to unwrap `Fail`+* dependencies added: `dlist`, `mtl`, `transformers`++### Removed++* `fsucc`: use `return` / `pure` instead+* `bindFail` and `composeFail`: use monadic fail instead++### Changed++* `ffail` and `fwarn` renamed to `afail` and `awarn`+* `afail` and `awarn` are more generic now+* documentation strictly improoved++## 0.0.3++First usable version
README.md view
@@ -3,12 +3,12 @@ Assume you have some type ```haskell-data Animal =- Animal+data Animal = Animal { species :: String , weight :: Double- , age :: NominalDiffTime- }+ , age :: Int+ } deriving (Show)+ ``` And you would like to produce this value from some data (e.g. query@@ -19,44 +19,51 @@ Like that: ```haskell-spc = "Parastratiosphecomyia stratiosphecomyioides"-w = 100-a = 27234--animal = Animal- <$> (if length spc > 20- then fwarn "Name is too long" spc- else if spc == ""- then ffail "Name can not be empty"- else fsucc spc)- <*> (if w < 0- then ffail "Weight can not be negative"- else fsucc w)- <*> (if a < 0- then ffail "Age can not be negative"- else fsucc a)+let spc = "Parastratiosphecomyia stratiosphecomyioides"+ w = 100+ a = 27234+ animal :: Fail [String] Animal+ animal = Animal+ <$> (if length spc > 20+ then awarn "Name is too long" spc+ else if spc == ""+ then afail "Name can not be empty"+ else pure spc)+ <*> (if w < 0+ then afail "Weight can not be negative"+ else pure w)+ <*> (if a < 0+ then afail "Age can not be negative"+ else pure a) ``` Now you can inspect the value we have got ```haskell-λ> animal+>>> animal Fail ["Name is too long"] (Just (Animal {species = "Parastratiosphecomyia stratiosphecomyioides", weight = 100.0, age = 27234}))-λ> getSucc animal++>>> getSucc animal Just (Animal {species = "Parastratiosphecomyia stratiosphecomyioides", weight = 100.0, age = 27234})-λ> getFail animal++>>> getFail animal Just ["Name is too long"] ``` -Here is another simple examples:+Now this example can be rewritten with monadic syntax inside field+checkers using module `Control.Monad.Fail`: ```haskell-λ> (,) <$> ffail "oups" <*> ffail "duh"-Fail ["oups","duh"] Nothing-λ> (,) <$> fwarn "oups" "hello" <*> fwarn "duh" "world"-Fail ["oups","duh"] (Just ("hello","world"))-λ> (,) <$> fsucc "hello" <*> fwarn "duh" "world"-Fail ["duh"] (Just ("hello","world"))-λ> (,) <$> fsucc "hello" <*> fsucc "world"-Success ("hello","world")+let animal :: Fail [String] Animal+ animal = Animal+ <$> (runFailI $ do+ when (length spc > 20) $ mwarn "Name is too long"+ when (spc == "") $ mfail "Name can not be empty"+ return spc)+ <*> (runFailI $ do+ when (w < 0) $ mfail "Weight can not be negative"+ return w)+ <*> (runFailI $ do+ when (a < 0) $ mfail "Age can not be negative"+ return a) ```
applicative-fail.cabal view
@@ -1,9 +1,9 @@ name: applicative-fail-version: 0.0.3-synopsis: Applicative functor which collects all your fails+version: 1.0.0+synopsis: Applicative functor and monad which collects all your fails description: Applicative functor to perform parse-like actions and- collect wanrings/failures+ collect wanrings/failures. license: BSD3 license-file: LICENSE@@ -15,7 +15,8 @@ cabal-version: >=1.10 -extra-source-files: README.md+extra-source-files: CHANGELOG.md+ , README.md homepage: https://bitbucket.org/s9gf4ult/applicative-fail source-repository head@@ -26,15 +27,53 @@ default-language: Haskell2010 hs-source-dirs: src - default-extensions: DeriveDataTypeable+ default-extensions: CPP+ , DeriveDataTypeable , DeriveFoldable , DeriveFunctor , DeriveGeneric , DeriveTraversable+ , FlexibleContexts+ , FlexibleInstances+ , GeneralizedNewtypeDeriving+ , LambdaCase+ , MultiParamTypeClasses+ , ScopedTypeVariables+ , StandaloneDeriving+ , TupleSections+ , TypeSynonymInstances+ , UndecidableInstances+ , ViewPatterns build-depends: base >=4.6 && <4.8 , bifunctors+ , dlist+ , mtl+ , transformers+ , transformers-base exposed-modules: Control.Applicative.Fail+ , Control.Monad.Fail++ ghc-options: -Wall++test-suite test+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: test++ default-extensions: FlexibleInstances+ , ScopedTypeVariables+ , TemplateHaskell++ main-is: Test.hs++ build-depends: base >= 3 && < 5+ , QuickCheck+ , applicative-fail+ , checkers+ , mtl+ , tasty+ , tasty-quickcheck ghc-options: -Wall
src/Control/Applicative/Fail.hs view
@@ -1,56 +1,93 @@ module Control.Applicative.Fail- ( Fail(..)- , ffail- , fwarn- , fsucc+ ( -- * Intro+ -- $intro++ -- * Fail+ Fail(..)+ , runFail+ , runDLFail+ , afail+ , awarn+ , fNull , getFail , getSucc -- * Combinators , failEither , joinFail- , bindFail- , composeFail ) where import Control.Applicative import Data.Bifunctor+import Data.DList ( DList ) import Data.Foldable import Data.Monoid import Data.Traversable import Data.Typeable import GHC.Generics +import qualified Data.DList as DL -{-| Applicative functor which collects all the fails instead of-immediate returning first fail like `Either`. It can not be a monad-because of differenct logic in Applicative. Applicative instance of-Fail continue to fold fails even when 'Fail e Nothing' pattern is-met. Monad instance can not behave like that, so 'Fail' have no Monad-instance+{- $intro -Example usage:+Assume you have some type ->>> (,,) <$> Fail [10] (Just 10) <*> Success 10 <*> Success 20-Fail [10] (Just (10,10,20))->>> (,) <$> Fail [1] Nothing <*> Success 10-Fail [1] Nothing->>> (,) <$> Fail [1] (Just 10) <*> Fail [2] (Just 20)-Fail [1,2] (Just (10,20))+>>> :{+data Animal = Animal+ { species :: String+ , weight :: Double+ , age :: Int+ } deriving (Show)+:} -or like that:+And you would like to produce this value from some data (e.g. query+parameters). There can be some warnigns or value can not be produced+at all. It would be great to have some simple tool to notify about+warnings and/or fail computation. ->>> (,) <$> ffail "oups" <*> fsucc 10-Fail ["oups"] Nothing->>> (,,) <$> fwarn "oups" 10 <*> fwarn "meh" 20 <*> fsucc 30-Fail ["oups","meh"] (Just (10,20,30))->>> (,,) <$> ffail "oups" <*> ffail "meh" <*> fsucc 30-Fail ["oups","meh"] Nothing+Like that: -This type is usefull for form parsing and returning your own type of-errors+>>> let spc = "Parastratiosphecomyia stratiosphecomyioides"+>>> let w = 100+>>> let a = 27234+>>> :{+let animal :: Fail [String] Animal+ animal = Animal+ <$> (if length spc > 20+ then awarn "Name is too long" spc+ else if spc == ""+ then afail "Name can not be empty"+ else pure spc)+ <*> (if w < 0+ then afail "Weight can not be negative"+ else pure w)+ <*> (if a < 0+ then afail "Age can not be negative"+ else pure a)+:} +>>> animal+Fail ["Name is too long"] (Just (Animal {species = "Parastratiosphecomyia stratiosphecomyioides", weight = 100.0, age = 27234}))++>>> getSucc animal+Just (Animal {species = "Parastratiosphecomyia stratiosphecomyioides", weight = 100.0, age = 27234})++>>> getFail animal+Just ["Name is too long"]++Now you can build your own parser-like things+ -} ++{- | Applicative functor which collects all the fails instead of+immediate returning first fail like `Either`. It can not be a monad+because of differenct logic in Applicative. Applicative instance of+Fail continue to fold fails even when 'Fail e Nothing' pattern is+met. Monad instance can not behave like that, so 'Fail' have no Monad+instance++-}+ data Fail e a = Fail e (Maybe a) -- ^ (Just a) when checking may proceed in Applicative | Success a@@ -75,29 +112,47 @@ mappend res@(Fail{}) (Success{}) = res -- fail always win mappend (Success{}) res@(Fail{}) = res -ffail :: e -> Fail [e] a-ffail e = Fail (pure e) Nothing+-- | Unwraps 'Fail' to tuple of error and value. If pattern is+-- 'Success' then return 'mempty' in error part.+runFail :: (Monoid e) => Fail e a -> (e, Maybe a)+runFail (Success a) = (mempty, Just a)+runFail (Fail e a) = (e, a) -fwarn :: e -> a -> Fail [e] a-fwarn e a = Fail [e] (Just a)+-- | Unwraps 'Fail' and constrain error container to 'DList' for type+-- inference+runDLFail :: Fail (DList e) a -> ([e], Maybe a)+runDLFail = first DL.toList . runFail -fsucc :: a -> Fail e a-fsucc a = Success a+-- | Return True if pattern does not contain not success value nor+-- fails, i.e. (Fail mempty Nothing)+fNull :: (Eq e, Monoid e) => Fail e a -> Bool+fNull (Fail ((== mempty) -> True) Nothing) = True+fNull _ = False +afail :: Applicative f => e -> Fail (f e) a+afail e = Fail (pure e) Nothing++awarn :: Applicative f => e -> a -> Fail (f e) a+awarn e a = Fail (pure e) (Just a)++-- | Return 'Right' if there is value (including pattern '(Fail e+-- (Just a))'). If there is no value return 'Left' failEither :: Fail e a -> Either e a failEither (Success a) = Right a failEither (Fail _ (Just a)) = Right a failEither (Fail e Nothing) = Left e +-- | Return fail part if exists getFail :: Fail e a -> Maybe e getFail (Fail e _) = Just e getFail _ = Nothing +-- | Return success part if exists getSucc :: Fail e a -> Maybe a getSucc (Success a) = Just a getSucc (Fail _ a) = a --- | Join two fails like monad does+-- | Join two fails like monad does. This function still match to 'Applicative' joinFail :: (Monoid e) => Fail e (Fail e a) -> Fail e a@@ -107,23 +162,3 @@ (a >>= getFail) aa = a >>= getSucc in Fail ee aa----- | This is a monadic-like bind. It breaks computation like--- Maybe and does not correspond to Applicative instance--- behaviour. So, instead of implementing Monad instance we--- just implement separate 'bind' operator-bindFail :: (Monoid e)- => Fail e a- -> (a -> Fail e b)- -> Fail e b-bindFail a f = joinFail $ fmap f a-infixl 1 `bindFail`--composeFail :: (Monoid e)- => (a -> Fail e b)- -> (b -> Fail e c)- -> a- -> Fail e c-composeFail l r a = bindFail (l a) r-infixl 1 `composeFail`
+ src/Control/Monad/Fail.hs view
@@ -0,0 +1,221 @@+module Control.Monad.Fail+ ( -- * Intro+ -- $intro++ -- * Failing monad+ FailT(..)+ , runFailC+ , runFailI+ -- * Helper functions+ , mfail+ , mwarn+ ) where++import Control.Applicative+import Control.Applicative.Fail+import Control.Monad+import Control.Monad.Base+import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer+import Data.Foldable+import Data.Functor.Compose+import Data.Functor.Identity+import Data.Monoid+import Data.Traversable+import Data.Tuple+import Data.Typeable+import GHC.Generics++#if MIN_VERSION_mtl(2,2,1)+import Control.Monad.Except+#else+import Control.Monad.Error+#endif++{- $intro++Failing monad transformer, which behaves in general like+'EitherT' but it also supports warnings. In short, it behaves like+combination of 'EitherT' and 'WriterT' transformers and built on+'Fail' applicative functor.++>>> runFailT $ do {a <- return 10; b <- return 20; return (a, b)}+Success (10,20)++>>> runFailT $ (,) <$> pure 10 <*> pure 20+Success (10,20)++>>> fmap runDLFail $ runFailT $ do {a <- mfail 10 ; b <- mfail 20; return (a, b)}+([10],Nothing)++>>> fmap runDLFail $ runFailT $ (,) <$> mfail 10 <*> mfail 20+([10],Nothing)++Note, that Applicative instance behaves just like Monad: it fails+immediately. 'FailT' also supports warning like 'Fail' does:++>>> fmap runDLFail $ runFailT $ do {a <- mwarn 10 *> return 15; b <- return 20; return (a, b)}+([10],Just (15,20))++>>> fmap runDLFail $ runFailT $ (,) <$> (mwarn 10 *> return 15) <*> return 20+([10],Just (15,20))++You can also combine 'FailT' with 'Fail' using 'Compose' like that:++>>> let check10 = do {liftBase $ print "checking 10"; return 10}+>>> let check20 = do {liftBase $ print "checking 20"; mwarn "oups"; return 20}+>>> fmap runDLFail $ getCompose $ (,) <$> runFailC check10 <*> runFailC check20+"checking 10"+"checking 20"+(["oups"],Just (10,20))++Note how 'Compose' functor is used here.++>>> let fail10 = do {liftBase $ print "failing 10"; mfail "10 is failed"}+>>> fmap runDLFail $ getCompose $ (,) <$> runFailC fail10 <*> runFailC check20+"failing 10"+"checking 20"+(["10 is failed","oups"],Nothing)++Note how second checker was runned even after first checker failed+(got "oups" message). This is because internal (monadic) checkers+unrolled back to __IO (Fail e a)__ and wrapped to 'Compose' so infered+type of __runFailC fail10__ is __Compose IO (Fail (DList String)) a__++Example from "Control.Applicative.Fail" can be also rewritten more convenient:++>>> :{+data Animal = Animal+ { species :: String+ , weight :: Double+ , age :: Int+ } deriving (Show)+:}++>>> let spc = "Parastratiosphecomyia stratiosphecomyioides"+>>> let w = 100+>>> let a = 27234+>>> :{+let animal :: Fail [String] Animal+ animal = Animal+ <$> (runFailI $ do+ when (length spc > 20) $ mwarn "Name is too long"+ when (spc == "") $ mfail "Name can not be empty"+ return spc)+ <*> (runFailI $ do+ when (w < 0) $ mfail "Weight can not be negative"+ return w)+ <*> (runFailI $ do+ when (a < 0) $ mfail "Age can not be negative"+ return a)+:}++>>> animal+Fail ["Name is too long"] (Just (Animal {species = "Parastratiosphecomyia stratiosphecomyioides", weight = 100.0, age = 27234}))++Monadic interface is much more comfortable here+-}+++newtype FailT e m a = FailT+ { runFailT :: m (Fail e a)+ } deriving ( Functor, Foldable, Traversable+ , Typeable, Generic )++-- | Unwraps 'FailT' and wraps result into 'Compose' functor. Usable+-- for convenient composition of 'Fail' where 'FailT' works inside.+runFailC :: FailT e m a -> Compose m (Fail e) a+runFailC = Compose . runFailT+{-# INLINEABLE runFailC #-}++runFailI :: FailT e Identity a -> Fail e a+runFailI = runIdentity . runFailT+{-# INLINEABLE runFailI #-}++deriving instance Eq (m (Fail e a)) => Eq (FailT e m a)+deriving instance Ord (m (Fail e a)) => Ord (FailT e m a)+deriving instance Show (m (Fail e a)) => Show (FailT e m a)++instance (Applicative m, Monoid a, Monoid e) => Monoid (FailT e m a) where+ mempty = FailT $ pure $ mempty+ {-# INLINEABLE mempty #-}+ mappend (FailT a) (FailT b) = FailT $ mappend <$> a <*> b+ {-# INLINEABLE mappend #-}++-- | NOTE: This instance behaves not like 'Applicative' for+-- 'Fail'. This applicative does not try to collect all posible fails,+-- it returns fast like 'EitherT' to match the 'Monad' isntance+-- behaviour.+instance (Monoid e, Functor m, Monad m) => Applicative (FailT e m) where+ pure a = return a+ {-# INLINEABLE pure #-}+ mf <*> ma = mf >>= \f -> fmap f ma+ {-# INLINEABLE (<*>) #-}++instance (Monoid e, Monad m) => Monad (FailT e m) where+ return a = FailT $ return $ pure a+ {-# INLINEABLE return #-}++ x >>= f = FailT $ runFailT x >>= \case+ Success a -> runFailT $ f a+ Fail e (Just a) -> runFailT (f a) >>= \case+ Success b -> return $ Fail e (Just b)+ Fail e' mb -> return $ Fail (e <> e') mb+ Fail e Nothing -> return $ Fail e Nothing+ {-# INLINEABLE (>>=) #-}++instance MonadTrans (FailT e) where+ lift ma = FailT $ liftM Success ma+ {-# INLINEABLE lift #-}++instance (Monoid e, MonadBase b m) => MonadBase b (FailT e m) where+ liftBase = lift . liftBase+ {-# INLINEABLE liftBase #-}++instance (MonadReader r m, Monoid e) => MonadReader r (FailT e m) where+ ask = lift ask+ {-# INLINEABLE ask #-}+ local f action = FailT $ do+ local f (runFailT action)+ {-# INLINEABLE local #-}+ reader = lift . reader+ {-# INLINEABLE reader #-}++instance (MonadState s m, Monoid e) => MonadState s (FailT e m) where+ get = lift get+ {-# INLINEABLE get #-}+ put = lift . put+ {-# INLINEABLE put #-}+ state = lift . state+ {-# INLINEABLE state #-}++instance (MonadWriter w m, Monoid e) => MonadWriter w (FailT e m) where+ writer = lift . writer+ {-# INLINEABLE writer #-}+ tell = lift . tell+ {-# INLINEABLE tell #-}+ listen action = FailT $ do+ (f, w) <- listen (runFailT action)+ return $ fmap (,w) f+ {-# INLINEABLE listen #-}+ pass action = FailT $ do+ a <- runFailT action+ let x = sequenceA $ fmap swap a+ pass $ return $ swap x+ {-# INLINEABLE pass #-}++instance (Monad m, Monoid e) => MonadError e (FailT e m) where+ throwError e = FailT $ return $ Fail e Nothing+ catchError ma handler = FailT $ runFailT ma >>= \case+ res@(Success _) -> return res+ (Fail e _) -> runFailT $ handler e+++mfail :: (Applicative f, Applicative m) => e -> FailT (f e) m a+mfail e = FailT $ pure $ afail e+{-# INLINEABLE mfail #-}++mwarn :: (Applicative f, Applicative m) => e -> FailT (f e) m ()+mwarn e = FailT $ pure $ awarn e ()+{-# INLINEABLE mwarn #-}
+ test/Test.hs view
@@ -0,0 +1,87 @@+{-# OPTIONS -fno-warn-orphans #-}+module Main where++import Control.Applicative+import Control.Applicative.Fail+import Control.Monad.Fail+import Control.Monad.Identity+import Test.QuickCheck hiding ( Success )+import Test.QuickCheck.Checkers+import Test.QuickCheck.Classes+import Test.Tasty+import Test.Tasty.QuickCheck hiding ( Success )++tastyUnbatch :: String -> TestBatch -> TestTree+tastyUnbatch name b =+ let u = unbatch b+ in testGroup name $ map (uncurry testProperty) u++instance (Arbitrary e, Arbitrary a) => Arbitrary (Fail e a) where+ arbitrary = oneof+ [ Success <$> arbitrary+ , Fail <$> arbitrary <*> arbitrary ]++instance (Eq e, Eq a) => EqProp (Fail e a) where+ a =-= b = eq a b++instance Arbitrary a => Arbitrary (Identity a) where+ arbitrary = Identity <$> arbitrary++instance (Arbitrary e, Arbitrary a) => Arbitrary (FailT e Identity a) where+ arbitrary = FailT <$> arbitrary++instance (Eq e, Eq a) => EqProp (FailT e Identity a) where+ a =-= b = eq a b++prop_FunctorFail :: TestTree+prop_FunctorFail = tastyUnbatch "Functor"+ $ functor (undefined :: Fail [Int] (Int, Int, Int))++prop_ApplicativeFail :: TestTree+prop_ApplicativeFail = tastyUnbatch "Applicative"+ $ applicative (undefined :: Fail [Int] (Int, Int, Int))++prop_MonoidFail :: TestTree+prop_MonoidFail = tastyUnbatch "Monoid"+ $ monoid (undefined :: Fail [Int] [Int])++prop_FunctorFailT :: TestTree+prop_FunctorFailT = tastyUnbatch "Function"+ $ functor (undefined :: FailT Int Identity (Int, Int, Int))++prop_ApplicativeFailT :: TestTree+prop_ApplicativeFailT = tastyUnbatch "Applicative"+ $ applicative (undefined :: FailT [Int] Identity (Int, Int, Int))++prop_MonoidFailT :: TestTree+prop_MonoidFailT = tastyUnbatch "Monoid"+ $ monoid (undefined :: FailT [Int] Identity [Int])++prop_MonadFailT :: TestTree+prop_MonadFailT = tastyUnbatch "Monad"+ $ monad (undefined :: FailT [Int] Identity (Int, Int, Int))++prop_MonadApplicativeFailT :: TestTree+prop_MonadApplicativeFailT = tastyUnbatch "Monad <-> Applicative"+ $ monadApplicative (undefined :: FailT [Int] Identity (Int, Int))++prop_MonadFunctorFailT :: TestTree+prop_MonadFunctorFailT = tastyUnbatch "Monad <-> Functor"+ $ monadFunctor (undefined :: FailT [Int] Identity (Int, Int))++main :: IO ()+main = defaultMain $ testGroup "properties"+ [ testGroup "Fail"+ [ prop_FunctorFail+ , prop_ApplicativeFail+ , prop_MonoidFail+ ]+ , testGroup "MFail"+ [ prop_FunctorFailT+ , prop_ApplicativeFailT+ , prop_MonoidFailT+ , prop_MonadFailT+ , prop_MonadFunctorFailT+ , prop_MonadApplicativeFailT+ ]+ ]