packages feed

tasty-laws 0.2 → 0.3

raw patch · 9 files changed

+358/−120 lines, 9 filesdep ~smallcheck-laws

Dependency ranges changed: smallcheck-laws

Files

CHANGELOG.md view
@@ -4,6 +4,14 @@ CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased][unreleased]+### Changed+- `Serial` instances instead `Proxy` where possible.++### Added+- Default tests for type constructors parametrized with `()`.+- Exhaustive `TestTree`s.+ ## [0.2] - 2015-09-04 ### Removed - `smallcheck` specific modules from@@ -13,4 +21,5 @@ ### Changed - Simplify module hierarchy: `Test.Tasty.SmallCheck.Laws` -> `Test.Tasty.Laws` +[unreleased]: https://github.com/jdnavarro/tasty-laws/compare/v0.2...HEAD [0.2]: https://github.com/jdnavarro/tasty-laws/compare/bf1caa5...v0.2
README.md view
@@ -3,7 +3,7 @@ [![Hackage Version](https://img.shields.io/hackage/v/tasty-laws.svg)](https://hackage.haskell.org/package/tasty-laws) [![Build Status](https://img.shields.io/travis/jdnavarro/tasty-laws.svg)](https://travis-ci.org/jdnavarro/tasty-laws) -Preassembled `tasty` runners for property testing the following laws:+Preassembled `tasty` `TestTree`s for property testing the following laws:  - Monoids - Functors
Test/Tasty/Laws/Applicative.hs view
@@ -1,7 +1,16 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}-module Test.Tasty.Laws.Applicative where+-- | This module is intended to be imported @qualified@, for example:+--+-- > import qualified Test.Tasty.Laws.Applicative as Applicative+--+module Test.Tasty.Laws.Applicative+  ( test+  , testMono+  , testMonoExhaustive+  , module Test.SmallCheck.Laws.Applicative+  ) where  #if !MIN_VERSION_base(4,8,0) import Control.Applicative (Applicative)@@ -11,39 +20,78 @@ import Test.Tasty (TestTree, testGroup) import Test.Tasty.SmallCheck (testProperty) import Test.SmallCheck.Series (Series, Serial(series))+import Test.SmallCheck.Laws.Applicative+  ( identity+  , composition+  , compositionSum+  , homomorphism+  , homomorphismSum+  , interchange+  , interchangeSum+  ) -import qualified Test.SmallCheck.Laws.Applicative as Applicative-import Test.Tasty.Laws.Functor+import qualified Test.Tasty.Laws.Functor as Functor --- | @tasty@ 'TestTree' for 'Applicative' laws. You need to provide the type---   wrapped in a `Proxy` and make sure 'a' is an instance of 'Serial'.-testApplicative+-- | @tasty@ 'TestTree' for 'Applicative' laws. The type signature forces the+--   parameter to be '()' which, unless you are dealing with non-total+--   functions, should be enough to test any 'Applicative's.+test+  :: ( Applicative f+     , Eq (f ()), Eq (f (f ())), Show (f ()), Show (f (() -> ()))+     , Serial Identity (f ())+     , Serial IO (f ()), Serial IO (f (() -> ()))+     )+  => Series IO (f ()) -> TestTree+test = testMonoExhaustive++-- | @tasty@ 'TestTree' for 'Applicative' laws. Monomorphic sum 'Series'.+testMono   :: forall f a .      ( Applicative f-     , Show a, Eq a-     , Show (f a), Eq (f a), (Eq (f (f a)))-     , Show (f (a -> a))-     , Serial IO a-     , Serial IO (f a)-     , Serial IO (a -> a)-     , Serial IO (f (a -> a))+     , Eq a, Eq (f a), (Eq (f (f a)))+     , Show a, Show (f a), Show (f (a -> a))      , Serial Identity a, Serial Identity (f a)+     , Serial IO a, Serial IO (f a), Serial IO (a -> a), Serial IO (f (a -> a))      )-  => Proxy (f a) -> TestTree-testApplicative proxy = testGroup "Applicative"-  [ testFunctor proxy-  , testProperty "pure id <*> v ≡ v"-  $ Applicative.identity (series :: Series IO (f a))-  , testProperty "(.) <$> u <*> v <*> w ≡  u <*> (v <*> w)"-  $ Applicative.composition+  => Series IO (f a) -> TestTree+testMono fs = testGroup "Applicative"+  [ Functor.testMono fs+  , testProperty "pure id <*> v ≡ v" $ identity fs+  , testProperty "(.) <$> u <*> v <*> w ≡  u <*> (v <*> w)" $ compositionSum       (series :: Series IO (f (a -> a)))       (series :: Series IO (f a))       (series :: Series IO (f (a -> a)))-  , testProperty "pure f <*> pure x ≡ pure (f x)" $ Applicative.homomorphism-      (Proxy :: Proxy f)+  , testProperty "pure f <*> pure x ≡ pure (f x)" $ homomorphismSum+      (Proxy  :: Proxy f)       (series :: Series IO a)       (series :: Series IO (a -> a))-  , testProperty "u <*> pure y ≡ pure ($ y) <*> u" $ Applicative.interchange+  , testProperty "u <*> pure y ≡ pure ($ y) <*> u" $ interchangeSum+      (series :: Series IO a)+      (series :: Series IO (f (a -> a)))+  ]++-- | @tasty@ 'TestTree' for 'Applicative' laws. Monomorphic product 'Series'.+testMonoExhaustive+  :: forall f a .+     ( Applicative f+     , Eq a, Eq (f a), (Eq (f (f a)))+     , Show a, Show (f a), Show (f (a -> a))+     , Serial Identity a, Serial Identity (f a)+     , Serial IO a, Serial IO (f a), Serial IO (a -> a), Serial IO (f (a -> a))+     )+  => Series IO (f a) -> TestTree+testMonoExhaustive fs = testGroup "Applicative"+  [ Functor.testMonoExhaustive fs+  , testProperty "pure id <*> v ≡ v" $ identity fs+  , testProperty "(.) <$> u <*> v <*> w ≡  u <*> (v <*> w)" $ composition+      (series :: Series IO (f (a -> a)))+      (series :: Series IO (f a))+      (series :: Series IO (f (a -> a)))+  , testProperty "pure f <*> pure x ≡ pure (f x)" $ homomorphism+      (Proxy  :: Proxy f)+      (series :: Series IO a)+      (series :: Series IO (a -> a))+  , testProperty "u <*> pure y ≡ pure ($ y) <*> u" $ interchange       (series :: Series IO a)       (series :: Series IO (f (a -> a)))   ]
Test/Tasty/Laws/Functor.hs view
@@ -1,29 +1,121 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}-module Test.Tasty.Laws.Functor where+-- | This module is intended to be imported @qualified@, for example:+--+-- > import qualified Test.Tasty.Laws.Functor as Functor+--+module Test.Tasty.Laws.Functor+  ( test+  , testMono+  , testMonoExhaustive+  , testPoly+  , testPolyExhaustive+  , module Test.SmallCheck.Laws.Functor+  ) where -import Data.Proxy (Proxy)+import Data.Proxy import Data.Functor.Identity (Identity) import Test.Tasty (TestTree, testGroup)-import Test.Tasty.SmallCheck (testProperty)+import Test.Tasty.SmallCheck (testProperty, Testable)+import Test.SmallCheck.Laws.Functor (identity, composition, compositionSum) import Test.SmallCheck.Series (Serial(series), Series) -import qualified Test.SmallCheck.Laws.Functor as Functor+-- | @tasty@ 'TestTree' for 'Functor' laws. The type signature forces the+--   parameter to be '()' which, unless you are dealing non-total functions,+--   should be enough to test any 'Functor's.+test :: (Functor f, Eq (f ()), Show (f ())) => Series IO (f ()) -> TestTree+test = testMonoExhaustive --- | @tasty@ 'TestTree' for 'Functor' laws. You need to provide the type---   wrapped in a `Proxy` and make sure 'a' is an instance of 'Serial'.-testFunctor+-- | @tasty@ 'TestTree' for 'Functor' laws. Monomorphic sum 'Series' for @f@+--   and @g@ in the compose law.+--+-- @+-- fmap (\a -> a) . (\a -> a) == fmap (\a -> a) . fmap (\a -> a)+-- fmap (\b -> b) . (\b -> b) == fmap (\b -> b) . fmap (\b -> b)+-- ...+-- @+testMono   :: forall f a .-     ( Eq (f a), Eq (f (f a)), Functor f, Show a, Show (f a)-     , Serial IO (f a)+     ( Eq (f a), Functor f, Show a, Show (f a)+     , Serial Identity a      , Serial IO (a -> a)-     , Serial Identity a, Serial Identity (f a)      )-  => Proxy (f a) -> TestTree-testFunctor _ = testGroup "Functor laws"-  [ testProperty "fmap id ≡ id" $ Functor.identity (series :: Series IO (f a))-  , testProperty "fmap (f . g) ≡ fmap f . fmap g" $ Functor.composition-      (series :: Series IO (f a))-      (series :: Series IO (a -> a))-      (series :: Series IO (a -> a))+  => Series IO (f a) -> TestTree+testMono = testWithComp $ \fs ->+    compositionSum fs (series :: Series IO (a -> a))+                      (series :: Series IO (a -> a))++-- | @tasty@ 'TestTree' for 'Functor' laws. Monomorphic product 'Series' for+--   @f@ and @g@ in the compose law.+--+-- @+-- fmap (\a -> a) . (\a -> a) == fmap (\a -> a) . fmap (\a -> a)+-- fmap (\a -> a) . (\a -> b) == fmap (\a -> a) . fmap (\a -> b)+-- fmap (\a -> a) . (\b -> b) == fmap (\a -> a) . fmap (\b -> b)+-- ...+-- @+testMonoExhaustive+  :: forall f a .+     ( Eq (f a), Functor f, Show a, Show (f a)+     , Serial Identity a+     , Serial IO (a -> a)+     )+  => Series IO (f a) -> TestTree+testMonoExhaustive = testWithComp $ \fs ->+    composition fs (series :: Series IO (a -> a))+                   (series :: Series IO (a -> a))++-- | @tasty@ 'TestTree' for 'Functor' laws. Polymorphic sum 'Series' for+--   @f@ and @g@ in the compose law.+--+-- @+-- fmap (\a0 -> b0) . (\b0 -> c0) == fmap (\a0 -> b0) . fmap (\b0 -> c0)+-- fmap (\a1 -> b1) . (\b1 -> c1) == fmap (\a1 -> a1) . fmap (\b1 -> c1)+-- fmap (\a2 -> b2) . (\b2 -> c2) == fmap (\a2 -> a2) . fmap (\b2 -> c2)+-- ...+-- @+testPoly+  :: forall f a b c .+     ( Functor f+     , Eq (f a), Show a, Show (f a) , Serial Identity a+     , Eq (f b), Show b, Show (f b) , Serial Identity b+     , Eq (f c), Show c, Show (f c) , Serial Identity c+     , Serial IO (a -> b), Serial IO (b -> c)+     )+  => Proxy b -> Proxy c -> Series IO (f a) -> TestTree+testPoly _ _ = testWithComp $ \fs ->+    compositionSum fs (series :: Series IO (b -> c))+                      (series :: Series IO (a -> b))++-- | @tasty@ 'TestTree' for 'Functor' laws. Polymorphic product 'Series' for+--   @f@ and @g@ in the compose law.+--+-- @+-- fmap (\a0 -> b0) . (\b0 -> c0) == fmap (\a0 -> b0) . fmap (\b0 -> c0)+-- fmap (\a0 -> b0) . (\b0 -> c1) == fmap (\a0 -> a0) . fmap (\b0 -> c1)+-- fmap (\a0 -> b0) . (\b0 -> c0) == fmap (\a0 -> a0) . fmap (\b1 -> c1)+-- ...+-- @+testPolyExhaustive+  :: forall f a b c .+     ( Functor f+     , Eq (f a), Show a, Show (f a) , Serial Identity a+     , Eq (f b), Show b, Show (f b) , Serial Identity b+     , Eq (f c), Show c, Show (f c) , Serial Identity c+     , Serial IO (a -> b), Serial IO (b -> c)+     )+  => Proxy b -> Proxy c -> Series IO (f a) -> TestTree+testPolyExhaustive _ _ = testWithComp $ \fs ->+    composition fs (series :: Series IO (b -> c))+                   (series :: Series IO (a -> b))++-- * Internal++testWithComp+  :: (Eq (f a), Functor f, Show (f a), Testable IO r)+  => (Series IO (f a) -> r) -> Series IO (f a) -> TestTree+testWithComp comp fs = testGroup "Functor laws"+  [ testProperty "fmap id ≡ id" $ identity fs+  , testProperty "fmap (f . g) ≡ fmap f . fmap g"+    $ comp fs   ]
Test/Tasty/Laws/Monad.hs view
@@ -1,36 +1,71 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}-module Test.Tasty.Laws.Monad where+-- | This module is intended to be imported @qualified@, for example:+--+-- > import qualified Test.Tasty.Laws.Monad as Monad+--+module Test.Tasty.Laws.Monad+  ( test+  , testMono+  , testMonoExhaustive+  ) where  #if !MIN_VERSION_base(4,8,0) import Control.Applicative (Applicative) #endif import Data.Functor.Identity (Identity)-import Data.Proxy (Proxy(..)) import Test.SmallCheck.Series (Series, Serial(series))+import Test.SmallCheck.Laws.Monad (associativity, associativitySum) import Test.Tasty (TestTree, testGroup) import Test.Tasty.SmallCheck (testProperty)-import qualified Test.SmallCheck.Laws.Monad as Monad+import qualified Test.Tasty.Laws.Applicative as Applicative -import Test.Tasty.Laws.Applicative+-- | @tasty@ 'TestTree' for 'Monad' laws. The type signature forces the+--   parameter to be '()' which, unless you are dealing with non-total+--   functions, should be enough to test any 'Monad's.+test+  :: ( Applicative m, Monad m+     , Eq (m ()), Eq (m (m ()))+     , Show (m ()), Show (m (() -> ()))+     , Serial Identity (m ())+     , Serial IO (m ()), Serial IO (m (() -> ()))+     )+  => Series IO (m ()) -> TestTree+test = testMonoExhaustive --- | @tasty@ 'TestTree' for 'Monad' laws. You need to provide the type---   wrapped in a `Proxy` and make sure 'a' is an instance of 'Serial'.-testMonad-  :: forall f a .-     ( Applicative f, Monad f-     , Show a, Show (f a), Show (f (a -> a))-     , Eq a, Eq (f a), Eq (f (f a))+-- | @tasty@ 'TestTree' for 'Monad' laws. Monomorphic sum 'Series'.+testMono+  :: forall m a .+     ( Applicative m, Monad m+     , Eq a, Eq (m a), Eq (m (m a))+     , Show a, Show (m a), Show (m (a -> a))+     , Serial Identity a, Serial Identity (m a)      , Serial IO a, Serial IO (a -> a)-     , Serial IO (f a) ,Serial IO (f (a -> a)), Serial IO (a -> f a)-     , Serial Identity a, Serial Identity (f a)+     , Serial IO (m a) ,Serial IO (m (a -> a)), Serial IO (a -> m a)      )-  => Proxy (f a) -> TestTree-testMonad proxy = testGroup "Monad laws"-  [ testApplicative proxy+  => Series IO (m a) -> TestTree+testMono ms = testGroup "Monad laws"+  [ Applicative.testMono ms   , testProperty "(m >>= f) >>= g ≡ m (f >=> g)"-  $ Monad.associativity (series :: Series IO (f a))-                        (series :: Series IO (a -> f a))-                        (series :: Series IO (a -> f a))+  $ associativitySum ms (series :: Series IO (a -> m a))+                        (series :: Series IO (a -> m a))+  ]++-- | @tasty@ 'TestTree' for 'Monad' laws. Monomorphic product 'Series'.+testMonoExhaustive+  :: forall m a .+     ( Applicative m, Monad m+     , Eq a, Eq (m a), Eq (m (m a))+     , Show a, Show (m a), Show (m (a -> a))+     , Serial Identity a, Serial Identity (m a)+     , Serial IO a, Serial IO (a -> a)+     , Serial IO (m a) ,Serial IO (m (a -> a)), Serial IO (a -> m a)+     )+  => Series IO (m a) -> TestTree+testMonoExhaustive ms = testGroup "Monad laws"+  [ Applicative.testMono ms+  , testProperty "(m >>= f) >>= g ≡ m (f >=> g)"+    $ associativity ms (series :: Series IO (a -> m a))+                       (series :: Series IO (a -> m a))   ]
Test/Tasty/Laws/Monoid.hs view
@@ -1,26 +1,63 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-module Test.Tasty.Laws.Monoid where+-- | This module is intended to be imported @qualified@:+--+-- > import qualified Test.Tasty.Laws.Monoid as Monoid+--+module Test.Tasty.Laws.Monoid+  ( test+  , testExhaustive+  , testMConcat+  , module Test.SmallCheck.Laws.Monoid+  ) where  #if !MIN_VERSION_base(4,8,0) import Data.Monoid (Monoid) #endif-import Data.Proxy (Proxy)-import Test.SmallCheck.Series (Series, Serial(series))++import Test.SmallCheck.Series (Series)+import Test.SmallCheck.Laws.Monoid+  ( leftIdentity+  , rightIdentity+  , associativity+  , associativitySum+  , mconcatProp+  ) import Test.Tasty (TestTree, testGroup) import Test.Tasty.SmallCheck (testProperty) -import qualified Test.SmallCheck.Laws.Monoid as Monoid+-- | @tasty@ 'TestTree' for 'Monoid' laws. Sum of series for associativity law.+--+-- @+-- ('a' <> 'a') <> 'a' == 'a' <> ('a' <> 'a')+-- ('b' <> 'b') <> 'b' == 'b' <> ('b' <> 'b')+-- ('c' <> 'c') <> 'c' == 'c' <> ('c' <> 'c')+-- ...+-- @+test :: (Eq a, Show a, Monoid a) => Series IO a -> TestTree+test ms = testGroup "Monoid laws"+  [ testProperty "mempty <> x ≡ x" $ leftIdentity ms+  , testProperty "x <> mempty ≡ x" $ rightIdentity ms+  , testProperty "x <> (y <> z) ≡ (x <> y) <> z"+        $ associativitySum ms ms ms+  ] --- | @tasty@ 'TestTree' for 'Applicative' laws. You need to provide the type---   wrapped in a `Proxy` and make sure 'a' is an instance of 'Serial'.-testMonoid :: forall a . (Show a, Eq a, Monoid a, Serial IO a) => Proxy a -> TestTree-testMonoid _ = testGroup "Monoid laws"-  [ testProperty "mempty <> x ≡ x" $ Monoid.leftIdentity (series :: Series IO a)-  , testProperty "x <> mempty ≡ x" $ Monoid.rightIdentity (series :: Series IO a)+-- | @tasty@ 'TestTree' for 'Monoid' laws. Product of series for associativity+--   law. Be aware of combinatorial explosion.+--+-- @+-- ('a' <> 'a') <> 'a' == 'a' <> ('a' <> 'a')+-- ('a' <> 'a') <> 'b' == 'a' <> ('a' <> 'b')+-- ('a' <> 'b') <> 'b' == 'a' <> ('b' <> 'b')+-- ...+-- @+testExhaustive :: (Eq a, Show a, Monoid a) => Series IO a -> TestTree+testExhaustive ms = testGroup "Monoid laws"+  [ testProperty "mempty <> x ≡ x" $ leftIdentity ms+  , testProperty "x <> mempty ≡ x" $ rightIdentity ms   , testProperty "x <> (y <> z) ≡ (x <> y) <> z"-  $ Monoid.associativity (series :: Series IO a) series series-  , testProperty "mconcat ≡ foldr mappend mempty"-  $ Monoid.mconcat (series :: Series IO a)+        $ associativity ms ms ms   ]++-- | Use this test when implementing the 'mconcat' method.+testMConcat :: (Eq a, Show a, Monoid a) => Series IO a -> TestTree+testMConcat ms = testProperty "mconcat ≡ foldr mappend mempty" $ mconcatProp ms
stack.yaml view
@@ -2,6 +2,6 @@ packages: - '.' extra-deps:-- smallcheck-laws-0.2+- smallcheck-laws-0.3 - smallcheck-series-0.5.1 resolver: lts-3.4
tasty-laws.cabal view
@@ -1,13 +1,15 @@ name:                tasty-laws-version:             0.2+version:             0.3 synopsis:            Test common laws description:-  Preassembled 'tasty' runners for property testing 'Monoid', 'Functor',+  Preassembled 'tasty' 'TestTree's for property testing 'Monoid', 'Functor',   'Applicative' and 'Monad' laws. license:             BSD3 license-file:        LICENSE author:              Danny Navarro maintainer:          j@dannynavarro.net+homepage:            https://github.com/jdnavarro/tasty-laws+bug-reports:         https://github.com/jdnavarro/tasty-laws/issues category:            Testing build-type:          Simple cabal-version:       >=1.10@@ -26,7 +28,7 @@                        Test.Tasty.Laws.Monoid   build-depends:       base >=4.6 && <4.9,                        smallcheck >=1.1.1,-                       smallcheck-laws >=0.1,+                       smallcheck-laws >=0.3,                        smallcheck-series >=0.3,                        tasty >=0.10,                        tasty-smallcheck >=0.8.0.1@@ -45,7 +47,7 @@   build-depends:       base >=4.6 && <4.9,                        smallcheck >=1.1.1,                        tasty >=0.10,-                       smallcheck-laws >=0.1,+                       smallcheck-laws >=0.3,                        tasty-laws    if impl(ghc < 7.8)
tests/tasty.hs view
@@ -8,15 +8,14 @@ import Control.Applicative ((<$>)) #endif import Data.Monoid (Sum(..), Product(..))-import Data.Proxy (Proxy(..)) -import Test.SmallCheck.Series (Serial(series))+import Test.SmallCheck.Series (Series, Serial(series)) import Test.Tasty (TestTree, defaultMain, testGroup) -import Test.Tasty.Laws.Applicative-import Test.Tasty.Laws.Functor-import Test.Tasty.Laws.Monad-import Test.Tasty.Laws.Monoid+import qualified Test.Tasty.Laws.Applicative as Applicative+import qualified Test.Tasty.Laws.Functor as Functor+import qualified Test.Tasty.Laws.Monad as Monad+import qualified Test.Tasty.Laws.Monoid as Monoid  main :: IO () main = defaultMain $ testGroup "Laws"@@ -26,74 +25,90 @@      , monadTests      ] +instance (Monad m, Serial m a) => Serial m (Sum a) where+    series = Sum <$> series++instance (Monad m, Serial m a) => Serial m (Product a) where+    series = Product <$> series+ monoidTests :: TestTree monoidTests = testGroup "Monoid"-  [ testGroup "Sum"-    [ testGroup "Int"-      [ testMonoid (Proxy :: Proxy (Sum Int)) ]+  [ testGroup "Product"+     [ testGroup "Int"+      [ Monoid.test (series :: Series IO (Product Int)) ]     , testGroup "Integer"-      [ testMonoid (Proxy :: Proxy (Sum Integer)) ]+      [ Monoid.test (series :: Series IO (Product Integer)) ]     , testGroup "Float"-      [ testMonoid (Proxy :: Proxy (Sum Float)) ]+      [ Monoid.test (series :: Series IO (Product Float)) ]     ]-  , testGroup "Product"-     [ testGroup "Int"-      [ testMonoid (Proxy :: Proxy (Product Int)) ]+  , testGroup "Exhausitive Sum"+    [ testGroup "Int"+      [ Monoid.testExhaustive (series :: Series IO (Sum Int)) ]     , testGroup "Integer"-      [ testMonoid (Proxy :: Proxy (Product Integer)) ]+      [ Monoid.testExhaustive (series :: Series IO (Sum Integer)) ]     , testGroup "Float"-      [ testMonoid (Proxy :: Proxy (Product Float)) ]+      [ Monoid.testExhaustive (series :: Series IO (Sum Float)) ]     ]   ]  functorTests :: TestTree functorTests = testGroup "Functor"   [ testGroup "Maybe"-    [ testGroup "Int"-      [ testFunctor (Proxy :: Proxy (Maybe Int)) ]+    [ testGroup "Unit"+      [ Functor.test (series :: Series IO (Maybe ())) ]+    , testGroup "Int"+      [ Functor.testMono (series :: Series IO (Maybe Int)) ]     , testGroup "Char"-      [ testFunctor (Proxy :: Proxy (Maybe Char)) ]+      [ Functor.testMono (series :: Series IO (Maybe Char)) ]+    , testGroup "Bool"+      [ Functor.testMonoExhaustive (series :: Series IO (Maybe Bool)) ]     ]   , testGroup "[]"-    [ testGroup "Bool"-      [ testFunctor (Proxy :: Proxy [Bool]) ]+    [ testGroup "Unit"+      [ Functor.test (series :: Series IO [()]) ]+    , testGroup "Bool"+      [ Functor.testMono (series :: Series IO [Bool]) ]     , testGroup "Int"-      [ testFunctor (Proxy :: Proxy [Int]) ]+      [ Functor.testMono (series :: Series IO [Int]) ]     ]   ]  applicativeTests :: TestTree applicativeTests = testGroup "Applicative"   [ testGroup "Maybe"-    [ testGroup "Int"-      [ testApplicative (Proxy :: Proxy (Maybe Int)) ]+    [ testGroup "Unit"+      [ Applicative.test (series :: Series IO (Maybe ())) ]+    , testGroup "Bool"+      [ Applicative.testMonoExhaustive (series :: Series IO (Maybe Bool)) ]+    , testGroup "Int"+      [ Applicative.testMono (series :: Series IO (Maybe Int)) ]     , testGroup "Float"-      [ testApplicative (Proxy :: Proxy (Maybe Float)) ]+      [ Applicative.testMono (series :: Series IO (Maybe Float)) ]     ]   , testGroup "[]"-    [ testGroup "Bool"-      [ testApplicative (Proxy :: Proxy [Bool]) ]-    , testGroup "Char"-      [ testApplicative (Proxy :: Proxy [Char]) ]+    [ testGroup "Unit"+      [ Applicative.test (series :: Series IO [()]) ]+    , testGroup "Bool"+      [ Applicative.testMono (series :: Series IO [Bool]) ]+    -- , testGroup "Char"+    --   [ Applicative.testMono (series :: Series IO [Char]) ]     ]   ]  monadTests :: TestTree monadTests = testGroup "Monad"   [ testGroup "Maybe"-    [ testGroup "()"-      [ testMonad (Proxy :: Proxy (Maybe ())) ]+    [ testGroup "Unit"+      [ Monad.test (series :: Series IO (Maybe ())) ]+    , testGroup "Bool"+      [ Monad.testMonoExhaustive (series :: Series IO (Maybe Bool)) ]     , testGroup "Int"-      [ testMonad (Proxy :: Proxy (Maybe Int)) ]+      [ Monad.testMono (series :: Series IO (Maybe Int)) ]     ]   , testGroup "[]"-    [ testGroup "()"-      [ testMonad (Proxy :: Proxy [()]) ]+    [ testGroup "Unit"+      [ Monad.test (series :: Series IO [()]) ]+    , testGroup "Bool"+      [ Monad.testMono (series :: Series IO [Bool]) ]     ]   ]--instance (Monad m, Serial m a) => Serial m (Sum a) where-    series = Sum <$> series--instance (Monad m, Serial m a) => Serial m (Product a) where-    series = Product <$> series