smallcheck-laws (empty) → 0.1
raw patch · 15 files changed
+614/−0 lines, 15 filesdep +basedep +smallcheckdep +smallcheck-lawssetup-changed
Dependencies added: base, smallcheck, smallcheck-laws, smallcheck-series, tagged, tasty, tasty-smallcheck, transformers
Files
- CHANGELOG.md +11/−0
- LICENSE +30/−0
- README.md +26/−0
- Setup.hs +2/−0
- Test/SmallCheck/Laws/Applicative.hs +85/−0
- Test/SmallCheck/Laws/Functor.hs +36/−0
- Test/SmallCheck/Laws/Monad.hs +48/−0
- Test/SmallCheck/Laws/Monoid.hs +64/−0
- Test/Tasty/SmallCheck/Laws/Applicative.hs +49/−0
- Test/Tasty/SmallCheck/Laws/Functor.hs +29/−0
- Test/Tasty/SmallCheck/Laws/Monad.hs +36/−0
- Test/Tasty/SmallCheck/Laws/Monoid.hs +26/−0
- smallcheck-laws.cabal +67/−0
- stack.yaml +6/−0
- tests/tasty.hs +99/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Change Log+All notable changes to this project will be documented in this file. This file+follows the formatting recommendations from [Keep a+CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic+Versioning](http://semver.org/).++## [0.1]+### Added+- Monoid Laws.++[0.1]: https://github.com/jdnavarro/smallcheck-laws/compare/ROOT...v0.1
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, J. Daniel Navarro++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of J. Daniel Navarro nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,26 @@+# SmallCheck Laws++[](https://hackage.haskell.org/package/smallcheck-laws) [](https://travis-ci.org/jdnavarro/smallcheck-laws)++Automatic `smallcheck` properties and `tasty` runners for:++ - Monoid laws.+ - Functor laws.+ - Applicative laws.+ - Monad laws.++## Installation++Make sure you have [`stack`](https://github.com/commercialhaskell/stack)+installed. Then type:++```sh+$ stack build+```+To run the test suite type:++```sh+$ stack test+```++<!-- TODO: Write minitutorial here about to use 'tasty' orphan instances -->
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Test/SmallCheck/Laws/Applicative.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Test.SmallCheck.Laws.Applicative+ (+ -- * Applicative laws+ identity+ , composition+ , homomorphism+ , interchange+ ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative (Applicative, (<$>), (<*>), pure)+#endif+import Data.Proxy (Proxy)+import Data.Functor.Identity (Identity)+import Test.SmallCheck (Property, over)+import Test.SmallCheck.Series (Serial, Series)+import Test.SmallCheck.Series.Utils (zipLogic, zipLogic3)++-- | Check the /identity/ law hold for the given 'Applicative' 'Series':+--+-- @+-- 'pure' id '<*>' v ≡ v+-- @+identity+ :: (Eq (f a), Monad m, Show (f a), Applicative f)+ => Series m (f a) -> Property m+identity xs = over xs $ \x -> (pure id <*> x) == x++-- | Check the /composition/ law hold for the given 'Applicative' 'Series':+--+-- @+-- '(.)' '<$>' u '<*>' v '<*>' w ≡ u '<*>' (v '<*>' w)+-- @+composition+ :: ( Eq (f b)+ , Monad m+ , Show (f c)+ , Show (f (a -> b))+ , Show (f (c -> a))+ , Applicative f+ )+ => Series m (f (c -> a))+ -> Series m (f c)+ -> Series m (f (a -> b))+ -> Property m+composition vs ws us = over (zipLogic3 vs ws us) $ \(v,w,u) ->+ (pure (.) <*> u <*> v <*> w) == (u <*> (v <*> w))++-- | Check the /homomorphism/ law hold for the given 'Applicative' 'Series':+--+-- @+-- 'pure' f '<*>' 'pure' x ≡ 'pure' (f x)+-- @+homomorphism+ :: forall m f a b .+ ( Monad m+ , Applicative f+ , Eq b+ , Eq (f b)+ , Show a, Show b+ , Serial Identity a+ , Serial Identity b+ )+ => Proxy f -> Series m a -> Series m (a -> b) -> Property m+homomorphism _ xs fs = over (zipLogic xs fs) $ \(x,f) ->+ (pure f <*> (pure x :: f a)) == pure (f x)++-- | Check the /interchange/ law hold for the given 'Applicative' 'Series':+--+-- @+-- u '<*>' 'pure' y ≡ 'pure' ($ y) '<*>' u+-- @+interchange+ :: ( Eq (f b)+ , Monad m+ , Show a+ , Show (f (a -> b))+ , Applicative f+ )+ => Series m a -> Series m (f (a -> b)) -> Property m+interchange ys us = over (zipLogic ys us) $ \(y,u) ->+ (u <*> pure y) == (pure ($ y) <*> u)
+ Test/SmallCheck/Laws/Functor.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE FlexibleContexts #-}+module Test.SmallCheck.Laws.Functor+ (+ -- * Functor laws+ identity+ , composition+ ) where++import Data.Functor.Identity (Identity)+import Test.SmallCheck (Property, over)+import Test.SmallCheck.Series (Serial, Series)+import Test.SmallCheck.Series.Utils (zipLogic3)++-- | Check the /identity/ law hold for the given 'Functor' 'Series':+--+-- @+-- 'fmap' 'id' ≡ 'id'+-- @+identity+ :: (Eq (f a), Monad m, Show (f a), Functor f)+ => Series m (f a) -> Property m+identity s = over s $ \x -> fmap id x == x++-- | Check the /composition/ law hold for the given 'Functor' 'Series':+--+-- @+-- 'fmap' (f . g) ≡ 'fmap' f . 'fmap' g+-- @+composition+ :: ( Monad m, Functor f, Show a, Show b, Show c+ , Show (f a), Eq (f c)+ , Serial Identity a, Serial Identity b+ )+ => Series m (f a) -> Series m (b -> c) -> Series m (a -> b) -> Property m+composition xs fs gs = over (zipLogic3 xs fs gs) $ \(x,f,g) ->+ fmap (f . g) x == (fmap f . fmap g) x
+ Test/SmallCheck/Laws/Monad.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleContexts #-}+{-|+Provided a 'Monad' is already an 'Applicative' there is no need to check the+first 2 monad laws. See+@<https://hackage.haskell.org/package/semigroupoids-5.0.0.2/docs/Data-Functor-Bind.html#t:Bind Bind>@+for which laws are exclusive for the '>>=' method.+-}+module Test.SmallCheck.Laws.Monad+ (+ -- * Monad laws+ associativity+ ) where++import Control.Monad ((>=>))+import Data.Functor.Identity (Identity)++import Test.SmallCheck (Property, over)+import Test.SmallCheck.Series (Serial, Series)+import Test.SmallCheck.Series.Utils (zipLogic3)++-- | Check the /associativity/ law hold for the given 'Monad' 'Series':+--+-- @+-- (m '>>=' f) '>>=' g ≡ m (f '>=>' g)+-- @+--+-- This is equivalent to:+--+-- @+-- (f '>=>' g) '>=>' h == f '>=>' (g '>=>' h)+-- @+--+-- Assuming 'join' is the default implementation of 'Monad'.++-- This is equivalent to `(f >=> g) >=> h == f >=> (g >=> h)` which requires+-- the constraint `Eq (a -> f b)`. `Eq (f a)` is much easier to deal with.+associativity+ :: ( Monad m, Monad f+ , Show a, Show b, Show c, Show (f a), Show (f b), Show (f c)+ , Eq (f a), Eq (f c)+ , Serial Identity a, Serial Identity b, Serial Identity c+ )+ => Series m (f a)+ -> Series m (a -> f b)+ -> Series m (b -> f c)+ -> Property m+associativity ms fs gs = over (zipLogic3 ms fs gs) $ \(m,f,g) ->+ (m >>= f >>= g) == (m >>= (f >=> g))
+ Test/SmallCheck/Laws/Monoid.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE CPP #-}+module Test.SmallCheck.Laws.Monoid+ (+ -- * Monoid laws+ leftIdentity+ , rightIdentity+ , associativity+ , mconcat+ ) where++#if MIN_VERSION_base(4,8,0)+import Prelude hiding (mconcat)+#else+import Data.Monoid (Monoid, mappend, mempty)+import Data.Traversable (sequenceA)+#endif+import Data.Monoid ((<>))+import qualified Data.Monoid as Monoid (mconcat)+import Test.SmallCheck (Property, over)+import Test.SmallCheck.Series (Series)+import Test.SmallCheck.Series.Utils (zipLogic3)++-- | Check the /left identity/ law hold for the given 'Monoid' 'Series':+--+-- @+-- 'mempty' '<>' x ≡ x+-- @+leftIdentity+ :: (Eq a, Monad m, Show a, Monoid a)+ => Series m a -> Property m+leftIdentity s = over s $ \x -> mempty <> x == x++-- | Check the /right identity/ law hold for the given 'Monoid' 'Series':+--+-- @+-- x '<>' 'mempty' ≡ x+-- @+rightIdentity+ :: (Eq a, Monad m, Show a, Monoid a)+ => Series m a -> Property m+rightIdentity s = over s $ \x -> x <> mempty == x++-- | Check the /associativity/ law hold for the given 'Monoid' 'Series':+--+-- @+-- x '<>' (y '<>' z) ≡ (x '<>' y) '<>' z+-- @+associativity+ :: (Eq a, Monad m, Show a, Monoid a)+ => Series m a -> Series m a -> Series m a -> Property m+associativity xs ys zs =+ over (zipLogic3 xs ys zs) $ \(x,y,z) ->+ x <> (y <> z) == (x <> y) <> z++-- | Check the /mconcat/ law hold for the given 'Monoid' 'Series':+--+-- @+-- 'mconcat' ≡ 'foldr' 'mappend' 'mempty'+-- @+mconcat+ :: (Eq a, Monad m, Show a, Monoid a)+ => Series m a -> Property m+mconcat s = over (sequenceA $ replicate 3 s) $ \l ->+ Monoid.mconcat l == foldr mappend mempty l
+ Test/Tasty/SmallCheck/Laws/Applicative.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Test.Tasty.SmallCheck.Laws.Applicative where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative (Applicative)+#endif+import Data.Functor.Identity (Identity)+import Data.Proxy (Proxy(..))+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.SmallCheck (testProperty)+import Test.SmallCheck.Series (Series, Serial(series))++import qualified Test.SmallCheck.Laws.Applicative as Applicative+import Test.Tasty.SmallCheck.Laws.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+ :: 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))+ , Serial Identity a, Serial Identity (f 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 :: 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)+ (series :: Series IO a)+ (series :: Series IO (a -> a))+ , testProperty "u <*> pure y ≡ pure ($ y) <*> u" $ Applicative.interchange+ (series :: Series IO a)+ (series :: Series IO (f (a -> a)))+ ]
+ Test/Tasty/SmallCheck/Laws/Functor.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Test.Tasty.SmallCheck.Laws.Functor where++import Data.Proxy (Proxy)+import Data.Functor.Identity (Identity)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.SmallCheck (testProperty)+import Test.SmallCheck.Series (Serial(series), Series)++import qualified Test.SmallCheck.Laws.Functor as Functor++-- | @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+ :: forall f a .+ ( Eq (f a), Eq (f (f a)), Functor f, Show a, Show (f a)+ , Serial IO (f 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))+ ]
+ Test/Tasty/SmallCheck/Laws/Monad.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Test.Tasty.SmallCheck.Laws.Monad 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.Tasty (TestTree, testGroup)+import Test.Tasty.SmallCheck (testProperty)++import Test.Tasty.SmallCheck.Laws.Applicative+import qualified Test.SmallCheck.Laws.Monad as Monad++-- | @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))+ , 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)+ )+ => Proxy (f a) -> TestTree+testMonad proxy = testGroup "Monad laws"+ [ testApplicative proxy+ , 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))+ ]
+ Test/Tasty/SmallCheck/Laws/Monoid.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Test.Tasty.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.Tasty (TestTree, testGroup)+import Test.Tasty.SmallCheck (testProperty)++import qualified Test.SmallCheck.Laws.Monoid as Monoid++-- | @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)+ , 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)+ ]
+ smallcheck-laws.cabal view
@@ -0,0 +1,67 @@+name: smallcheck-laws+version: 0.1+synopsis: SmallCheck properties for standard type classes+description:+ Automatic `smallcheck` properties and `tasty` runners for:+ .+ - Monoid laws.+ - Functor laws.+ - Applicative laws.+ - Monad laws.+ .+ Use the @<https://hackage.haskell.org/package/tasty Tasty>@ modules for+ preassembled tasty runners, your data types need to be instances of+ @<https://hackage.haskell.org/package/smallcheck-1.1.1/docs/Test-SmallCheck-Series.html#t:Serial Serial>@.+ For more granular control on how the+ @<https://hackage.haskell.org/package/smallcheck SmallCheck>@+ @<https://hackage.haskell.org/package/smallcheck-1.1.1/docs/Test-SmallCheck-Series.html#t:Series Series>@+ are created use the modules under @Test.SmallCheck.Laws@.+license: BSD3+license-file: LICENSE+author: Danny Navarro+maintainer: j@dannynavarro.net+category: Testing+build-type: Simple+cabal-version: >=1.10+extra-source-files: README.md CHANGELOG.md+ stack.yaml++source-repository head+ type: git+ location: git://github.com/jdnavarro/smallcheck-laws.git++library+ ghc-options: -Wall+ default-language: Haskell2010+ exposed-modules: Test.SmallCheck.Laws.Applicative,+ Test.SmallCheck.Laws.Functor,+ Test.SmallCheck.Laws.Monad,+ Test.SmallCheck.Laws.Monoid,+ Test.Tasty.SmallCheck.Laws.Applicative,+ Test.Tasty.SmallCheck.Laws.Functor,+ Test.Tasty.SmallCheck.Laws.Monad,+ Test.Tasty.SmallCheck.Laws.Monoid+ build-depends: base >=4.6 && <4.9,+ smallcheck >=1.1.1,+ smallcheck-series >=0.3,+ tasty >=0.10,+ tasty-smallcheck >=0.8+ if impl(ghc < 7.10)+ build-depends: transformers >=0.3.0.0++ if impl(ghc < 7.8)+ build-depends: tagged >=0.7.2++test-suite tasty+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: tasty.hs+ ghc-options: -Wall -threaded+ build-depends: base >=4.6 && <4.9,+ smallcheck >=1.1,+ tasty >=0.10,+ smallcheck-laws++ if impl(ghc < 7.8)+ build-depends: tagged >=0.7.2
+ stack.yaml view
@@ -0,0 +1,6 @@+flags: {}+packages:+- '.'+extra-deps:+- smallcheck-series-0.3+resolver: lts-2.21
+ tests/tasty.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Main where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif+import Data.Monoid (Sum(..), Product(..))+import Data.Proxy (Proxy(..))++import Test.SmallCheck.Series (Serial(series))+import Test.Tasty (TestTree, defaultMain, testGroup)++import Test.Tasty.SmallCheck.Laws.Applicative+import Test.Tasty.SmallCheck.Laws.Functor+import Test.Tasty.SmallCheck.Laws.Monad+import Test.Tasty.SmallCheck.Laws.Monoid++main :: IO ()+main = defaultMain $ testGroup "Laws"+ [ monoidTests+ , functorTests+ , applicativeTests+ , monadTests+ ]++monoidTests :: TestTree+monoidTests = testGroup "Monoid"+ [ testGroup "Sum"+ [ testGroup "Int"+ [ testMonoid (Proxy :: Proxy (Sum Int)) ]+ , testGroup "Integer"+ [ testMonoid (Proxy :: Proxy (Sum Integer)) ]+ , testGroup "Float"+ [ testMonoid (Proxy :: Proxy (Sum Float)) ]+ ]+ , testGroup "Product"+ [ testGroup "Int"+ [ testMonoid (Proxy :: Proxy (Product Int)) ]+ , testGroup "Integer"+ [ testMonoid (Proxy :: Proxy (Product Integer)) ]+ , testGroup "Float"+ [ testMonoid (Proxy :: Proxy (Product Float)) ]+ ]+ ]++functorTests :: TestTree+functorTests = testGroup "Functor"+ [ testGroup "Maybe"+ [ testGroup "Int"+ [ testFunctor (Proxy :: Proxy (Maybe Int)) ]+ , testGroup "Char"+ [ testFunctor (Proxy :: Proxy (Maybe Char)) ]+ ]+ , testGroup "[]"+ [ testGroup "Bool"+ [ testFunctor (Proxy :: Proxy [Bool]) ]+ , testGroup "Int"+ [ testFunctor (Proxy :: Proxy [Int]) ]+ ]+ ]++applicativeTests :: TestTree+applicativeTests = testGroup "Applicative"+ [ testGroup "Maybe"+ [ testGroup "Int"+ [ testApplicative (Proxy :: Proxy (Maybe Int)) ]+ , testGroup "Float"+ [ testApplicative (Proxy :: Proxy (Maybe Float)) ]+ ]+ , testGroup "[]"+ [ testGroup "Bool"+ [ testApplicative (Proxy :: Proxy [Bool]) ]+ , testGroup "Char"+ [ testApplicative (Proxy :: Proxy [Char]) ]+ ]+ ]++monadTests :: TestTree+monadTests = testGroup "Monad"+ [ testGroup "Maybe"+ [ testGroup "()"+ [ testMonad (Proxy :: Proxy (Maybe ())) ]+ , testGroup "Int"+ [ testMonad (Proxy :: Proxy (Maybe Int)) ]+ ]+ , testGroup "[]"+ [ testGroup "()"+ [ testMonad (Proxy :: Proxy [()]) ]+ ]+ ]++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