packages feed

exotic-list-monads 1.1.0 → 1.1.1

raw patch · 5 files changed

+111/−111 lines, 5 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Control.Monad.List.Exotic: instance forall k (m :: k -> *) (a :: k). GHC.Classes.Eq (m a) => GHC.Classes.Eq (Control.Monad.List.Exotic.DualListMonad m a)
+ Control.Monad.List.Exotic: instance forall k (m :: k -> *) (a :: k). GHC.Show.Show (m a) => GHC.Show.Show (Control.Monad.List.Exotic.DualListMonad m a)
- Control.Monad.List.Exotic: class (ListMonad m) => FreeRBPM m (c :: * -> Constraint) | m -> c
+ Control.Monad.List.Exotic: class (ListMonad m) => FreeRBPM m (c :: Type -> Constraint) | m -> c
- Control.Monad.List.NonEmpty.Exotic: class (NonEmptyMonad m) => FreeRBM m (c :: * -> Constraint) | m -> c
+ Control.Monad.List.NonEmpty.Exotic: class (NonEmptyMonad m) => FreeRBM m (c :: Type -> Constraint) | m -> c

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # exotic-list-monad changelog +## v1.1.1++- Refactor to avoid the noncanonical-monad-instances and star-is-type warnings++- Add Eq and Show instances to DualListMonad++- Fixes in documentation+ ## v1.1.0  - Add the AtMost monad
exotic-list-monads.cabal view
@@ -7,7 +7,7 @@ -- hash: 5b5385de8f22ca1f51a19de7b8b393b9012fded676d9ea7412ac96fde7fc9d83  name:           exotic-list-monads-version:        1.1.0+version:        1.1.1 synopsis:       Non-standard monads on lists and non-empty lists description:    The usual list monad is only one of infinitely many ways to turn the list functor into a monad. The same applies to the usual non-empty list monad and the non-empty list functor. This library collects such non-standard "list" and "non-empty list" monads. category:       List, Monads
src/Control/Monad/List/Exotic.hs view
@@ -174,6 +174,7 @@  import Prelude hiding ((<>)) import Control.Monad (ap, join)+import Data.Kind (Type) import GHC.Exts (IsList(..), IsString(..), Constraint) import GHC.TypeLits import Data.Proxy@@ -212,25 +213,20 @@ -- | Every list monad has a dual, in which join is defined as -- -- @--- join . reverse . fmap reverse+-- reverse . join . reverse . fmap reverse -- @ ----- (where join is the join of the original list monad), while return is------ @--- reverse . return--- @+-- (where join is the join of the original list monad). ----- (where return is the return of the original list monad).+-- return is the same as in the original monad. newtype DualListMonad m a = DualListMonad { unDualListMonad :: m a }- deriving (Functor)+ deriving (Functor, Show, Eq)  instance (ListMonad m) => Applicative (DualListMonad m) where-  pure  = return+  pure  = DualListMonad . pure   (<*>) = ap  instance (ListMonad m) => Monad (DualListMonad m) where-  return = DualListMonad . liftListFun reverse . return   DualListMonad m >>= f = DualListMonad $ liftListFun reverse $     liftListFun reverse m >>= liftListFun reverse . unDualListMonad . f @@ -278,7 +274,7 @@ -- | A class for __free right-braketed__ (subclasses of) -- __pointed magmas__. ----- Most of the monads defined in this module arise from subclasses of+-- All monads defined in this section arise from subclasses of -- 'PointedMagma', in which we do not assume any additional methods, -- but require the instances to satisfy additional equations. This -- means that the monad is not only an instance of such a class that@@ -321,7 +317,7 @@ -- @ -- instance FreeRBPM [] 'Data.Monoid.Monoid' -- @-class (ListMonad m) => FreeRBPM m (c :: * -> Constraint) | m -> c where+class (ListMonad m) => FreeRBPM m (c :: Type -> Constraint) | m -> c where   foldRBPM :: (PointedMagma a, c a) => (x -> a) -> m x -> a   foldRBPM _ (unwrap -> []) = eps   foldRBPM f (unwrap -> xs) = foldr1 (<>) (map f xs)@@ -345,8 +341,8 @@  -- | The Global Failure monad arises from free zero semigroups. It -- implements a kind of nondeterminism similar to the usual List--- monad, but failing (= producing an empty list) in one branch makes--- the entire computation fail.  Its join is defined as:+-- monad, but failing (= resulting in the empty list) in one branch+-- makes the entire computation fail.  Its join is defined as: -- -- @ -- join xss | any null xss = []@@ -365,11 +361,10 @@ deriving instance IsString (GlobalFailure Char)  instance Applicative GlobalFailure where-  pure  = return-  (<*>) = ap+  pure x = GlobalFailure [x]+  (<*>)  = ap  instance Monad GlobalFailure where-  return x = GlobalFailure [x]   GlobalFailure xs >>= f = GlobalFailure $ join $ map (unGlobalFailure . f) xs     where     join xss | any null xss = []@@ -460,11 +455,10 @@ deriving instance IsString (MazeWalk Char)  instance Applicative MazeWalk where-  pure  = return-  (<*>) = ap+  pure x = MazeWalk [x]+  (<*>)  = ap  instance Monad MazeWalk where-  return x = MazeWalk [x]   MazeWalk xs >>= f = MazeWalk $ join $ map (unMazeWalk . f) xs     where     join xss | null xss || any null xss@@ -532,11 +526,10 @@ deriving instance IsString (DiscreteHybrid Char)  instance Applicative DiscreteHybrid where-  pure  = return-  (<*>) = ap+  pure x = DiscreteHybrid [x]+  (<*>)  = ap  instance Monad DiscreteHybrid where-  return x = DiscreteHybrid [x]   DiscreteHybrid xs >>= f = DiscreteHybrid $ join $ map (unDiscreteHybrid . f) xs     where     join xss | null xss        = []@@ -601,11 +594,10 @@ deriving instance IsString (ListUnfold Char)  instance Applicative ListUnfold where-  pure  = return-  (<*>) = ap+  pure x = ListUnfold [x]+  (<*>)  = ap  instance Monad ListUnfold where-  return x = ListUnfold [x]   ListUnfold xs >>= f = ListUnfold $ join $ map (unListUnfold . f) xs     where     join xss | null xss || any null xss@@ -674,7 +666,7 @@ -- -- The 'Stutter' monad is quite similar to 'ListUnfold'. The -- difference is that when the latter fails (that is, its join results--- in an empty list), the former stutters on the last singleton.+-- in the empty list), the former stutters on the last singleton. -- -- Examples: --@@ -690,11 +682,10 @@ deriving instance (KnownNat n) => IsString (Stutter n Char)  instance (KnownNat n) => Applicative (Stutter n) where-  pure  = return-  (<*>) = ap+  pure x = Stutter [x]+  (<*>)  = ap  instance (KnownNat n) => Monad (Stutter n) where-  return x = Stutter [x]   Stutter xs >>= f = Stutter $ join $ map (unStutter . f) xs    where     join xss | null xss@@ -736,7 +727,7 @@  -- | The stutter-keeper monad arises from free stutter-keeper -- algebras. Its join stutters (as in the 'Stutter' monad) if the--- first non-singleton list in empty. Otherwise, it keeps the+-- first non-singleton list is empty. Otherwise, it keeps the -- singleton prefix, and keeps the first non-singleton list. The join -- can thus be defined as follows (omitting the conversion of the -- type-level 'Nat' @n@ to a run-time value):@@ -765,11 +756,10 @@ deriving instance (KnownNat n) => IsString (StutterKeeper n Char)  instance (KnownNat n) => Applicative (StutterKeeper n) where-  pure  = return-  (<*>) = ap+  pure x = StutterKeeper [x]+  (<*>)  = ap  instance (KnownNat n) => Monad (StutterKeeper n) where-  return x = StutterKeeper [x]   StutterKeeper xs >>= f = StutterKeeper $ join $ map (unStutterKeeper . f) xs    where     join xss | null xss@@ -844,11 +834,10 @@ deriving instance (KnownNat n, KnownNat m) => IsString (StutterStutter n m Char)  instance (KnownNat n, KnownNat m) => Applicative (StutterStutter n m) where-  pure  = return-  (<*>) = ap+  pure x = StutterStutter [x]+  (<*>)  = ap  instance (KnownNat n, KnownNat m) => Monad (StutterStutter n m) where-  return x = StutterStutter [x]   StutterStutter xs >>= f = StutterStutter $ join $ map (unStutterStutter . f) xs    where     join xss | null xss@@ -922,14 +911,14 @@ -- -- Below, we first show a couple of concrete examples of monads -- arising from particular numerical monoids, and then the general--- version via a set of generators.+-- version via a set of generators, @'NumericalMonoidMonad'@.  -------------------- -- The Mini monad -- --------------------  -- | The Mini monad is, in a sense, a minimal list monad, meaning that--- its join fails (= results in an empty list) for all values except+-- its join fails (= results in the empty list) for all values except -- the ones that appear in the unit laws (i.e., a singleton or a list -- of singletons): --@@ -958,11 +947,10 @@ deriving instance IsString (Mini Char)  instance Applicative Mini where-  pure  = return-  (<*>) = ap+  pure x =  Mini [x]+  (<*>)  = ap  instance Monad Mini where-  return x = Mini [x]   Mini xs >>= f = Mini $ join $ map (unMini . f) xs     where     join xss | isSingle xss || all isSingle xss = concat xss@@ -982,7 +970,7 @@ -- | The join of the Odd monad is a concat of the inner lists provided -- there is an odd number of them, and that all of them are of odd -- length themselves. Otherwise (modulo cases needed for the unit--- laws), it returns an empty list.+-- laws), the result is the empty list. -- -- @ -- join xss | isSingle xss || all isSingle xss  = concat xss@@ -1011,11 +999,10 @@ deriving instance IsString (Odd Char)  instance Applicative Odd where-  pure  = return-  (<*>) = ap+  pure x = Odd [x]+  (<*>)  = ap  instance Monad Odd where-  return x = Odd [x]   Odd xs >>= f = Odd $ join $ map (unOdd . f) xs     where     join xss | isSingle xss || all isSingle xss@@ -1071,11 +1058,10 @@ deriving instance (KnownNat n) => IsString (AtLeast n Char)  instance (KnownNat n) => Applicative (AtLeast n) where-  pure  = return-  (<*>) = ap+  pure x = AtLeast [x]+  (<*>)  = ap  instance (KnownNat n) => Monad (AtLeast n) where-  return x = AtLeast [x]   AtLeast xs >>= f = AtLeast $ join $ map (unAtLeast . f) xs     where     join xss | isSingle xss     = concat xss@@ -1148,11 +1134,10 @@ deriving instance IsString (NumericalMonoidMonad ns Char)  instance (NumericalMonoidGenerators ns) => Applicative (NumericalMonoidMonad ns) where-  pure  = return-  (<*>) = ap+  pure x = NumericalMonoidMonad [x]+  (<*>)  = ap  instance (NumericalMonoidGenerators ns) => Monad (NumericalMonoidMonad ns) where-  return x = NumericalMonoidMonad [x]   NumericalMonoidMonad xs >>= f = NumericalMonoidMonad $ join $ map (unNumericalMonoidMonad . f) xs     where     join xss | isSingle xss || all isSingle xss                          = concat xss@@ -1204,11 +1189,10 @@ deriving instance (KnownNat n) => IsString (AtMost n Char)  instance (KnownNat n) => Applicative (AtMost n) where-  pure  = return-  (<*>) = ap+  pure x = AtMost [x]+  (<*>)  = ap  instance (KnownNat n) => Monad (AtMost n) where-  return x = AtMost [x]   AtMost xs >>= f = AtMost $ join $ map (unAtMost . f) xs     where     join xss | isSingle xss || all isSingle xss                                = concat xss@@ -1298,11 +1282,10 @@ deriving instance IsString (ContinuumOfMonads s Char)  instance (SetOfNats s) => Applicative (ContinuumOfMonads s) where-  pure  = return-  (<*>) = ap+  pure x = ContinuumOfMonads [x]+  (<*>)  = ap  instance (SetOfNats s) => Monad (ContinuumOfMonads s) where-  return x = ContinuumOfMonads [x]   ContinuumOfMonads xs >>= f = ContinuumOfMonads $ join $ map (unContinuumOfMonads . f) xs     where     join xss | isSingle xss || all isSingle xss               = concat xss@@ -1352,11 +1335,10 @@ deriving instance (KnownNat n, KnownNat p) => IsString (ShortStutterKeeper n p Char)  instance (KnownNat n, KnownNat p) => Applicative (ShortStutterKeeper n p) where-  pure  = return-  (<*>) = ap+  pure x = ShortStutterKeeper [x]+  (<*>)  = ap  instance (KnownNat n, KnownNat p) => Monad (ShortStutterKeeper n p) where-  return x = ShortStutterKeeper [x]   ShortStutterKeeper xs >>= f = ShortStutterKeeper $ join $ map (unShortStutterKeeper . f) xs    where     join :: forall x. [[x]] -> [x]
src/Control/Monad/List/NonEmpty/Exotic.hs view
@@ -170,6 +170,7 @@ import qualified Data.List.NonEmpty as NonEmpty import Prelude hiding ((<>)) import Control.Monad (ap, join)+import Data.Kind (Type) import GHC.Exts (IsList(..), IsString(..), Constraint) import GHC.TypeLits import Data.Proxy@@ -284,7 +285,7 @@ -- @ -- instance FreeRBM 'NonEmpty' 'Data.Semigroup.Semigroup' -- @-class (NonEmptyMonad m) => FreeRBM m (c :: * -> Constraint) | m -> c where+class (NonEmptyMonad m) => FreeRBM m (c :: Type -> Constraint) | m -> c where   foldRBM :: (Magma a, c a) => (x -> a) -> m x -> a   foldRBM f (unwrap -> toList -> xs) = foldr1 (<>) (map f xs) @@ -319,11 +320,10 @@  deriving (Functor, Show, Eq)  instance Applicative Keeper where-  pure  = return-  (<*>) = ap+  pure a = Keeper $ [a]  -- OverloadedLists+  (<*>)  = ap  instance Monad Keeper where-  return a = Keeper $ [a]  -- OverloadedLists   Keeper xs >>= f =     Keeper $ join $ NonEmpty.map (unKeeper . f) xs    where@@ -380,11 +380,10 @@  deriving (Functor, Show, Eq)  instance Applicative DiscreteHybridNE where-  pure  = return-  (<*>) = ap+  pure a = DiscreteHybridNE $ [a]  -- OverloadedLists+  (<*>)  = ap  instance Monad DiscreteHybridNE where-  return a = DiscreteHybridNE $ [a]  -- OverloadedLists   DiscreteHybridNE xs >>= f =     DiscreteHybridNE $ join $ NonEmpty.map (unDiscreteHybridNE . f) xs    where@@ -439,11 +438,10 @@  deriving (Functor, Show, Eq)  instance Applicative OpDiscreteHybridNE where-  pure  = return-  (<*>) = ap+  pure a = OpDiscreteHybridNE $ [a]  -- OverloadedLists+  (<*>)  = ap  instance Monad OpDiscreteHybridNE where-  return a = OpDiscreteHybridNE $ [a]  -- OverloadedLists   OpDiscreteHybridNE xs >>= f =     OpDiscreteHybridNE $ join $ NonEmpty.map (unOpDiscreteHybridNE . f) xs    where@@ -499,11 +497,10 @@  deriving (Functor, Show, Eq)  instance Applicative MazeWalkNE where-  pure  = return-  (<*>) = ap+  pure a = MazeWalkNE $ [a]  -- OverloadedLists+  (<*>)  = ap  instance Monad MazeWalkNE where-  return a = MazeWalkNE $ [a]  -- OverloadedLists   MazeWalkNE xs >>= f =     MazeWalkNE $ join $ NonEmpty.map (unMazeWalkNE . f) xs    where@@ -559,11 +556,10 @@  deriving (Functor, Show, Eq)  instance (KnownNat n) => Applicative (StutterNE n) where-  pure  = return-  (<*>) = ap+  pure a = StutterNE $ [a]  -- OverloadedLists+  (<*>)  = ap  instance (KnownNat n) => Monad (StutterNE n) where-  return a = StutterNE $ [a]  -- OverloadedLists   StutterNE xs >>= f =     StutterNE $ join $ NonEmpty.map (unStutterNE . f) xs    where@@ -665,11 +661,10 @@  deriving (Functor, Show, Eq)  instance Applicative HeadTails where-  pure  = return-  (<*>) = ap+  pure a = HeadTails $ [a,a]  -- OverloadedLists+  (<*>)  = ap  instance Monad HeadTails where-  return a = HeadTails $ [a,a]  -- OverloadedLists   HeadTails xs >>= f = HeadTails $ join $ NonEmpty.map (unHeadTails . f) xs    where     join ((x :| _) :| xss) = x :| concatMap NonEmpty.tail xss@@ -770,11 +765,10 @@  deriving (Functor, Show, Eq)  instance Applicative HeadsTail where-  pure  = return-  (<*>) = ap+  pure a = HeadsTail $ [a,a]  -- OverloadedLists+  (<*>)  = ap  instance Monad HeadsTail where-  return a = HeadsTail $ [a,a]  -- OverloadedLists   HeadsTail xs >>= f = HeadsTail $ join $ NonEmpty.map (unHeadsTail . f) xs    where     join xss@(splitSnoc -> (xss', xs@(_:|ys)))@@ -840,11 +834,10 @@  deriving (Functor, Show, Eq)  instance Applicative AlphaOmega where-  pure  = return-  (<*>) = ap+  pure a = AlphaOmega [a]                           -- OverloadedLists+  (<*>)  = ap  instance Monad AlphaOmega where-  return a = AlphaOmega [a]                          -- OverloadedLists   AlphaOmega xs >>= f = AlphaOmega $ join $ NonEmpty.map (unAlphaOmega . f) xs    where     join xss | isSingle xss || nonEmptyAll isSingle xss@@ -882,26 +875,21 @@ -- as -- -- @--- join . reverse . fmap reverse+-- reverse . join . reverse . fmap reverse -- @ ----- (where join is the join of the original list monad), while return is------ @--- reverse . return--- @+-- (where join is the join of the original list monad). ----- (where return is the return of the original list monad).+-- return is the same as in the original monad. newtype DualNonEmptyMonad m a =   DualNonEmptyMonad { unDualNonEmptyMonad :: m a }  deriving (Functor, Show, Eq)  instance (NonEmptyMonad m) => Applicative (DualNonEmptyMonad m) where-  pure  = return-  (<*>) = ap+  pure = DualNonEmptyMonad . liftNEFun NonEmpty.reverse . pure+  (<*>)  = ap  instance (NonEmptyMonad m) => Monad (DualNonEmptyMonad m) where-  return = DualNonEmptyMonad . liftNEFun NonEmpty.reverse . return   DualNonEmptyMonad m >>= f = DualNonEmptyMonad $ liftNEFun NonEmpty.reverse $     liftNEFun NonEmpty.reverse m >>=       liftNEFun NonEmpty.reverse . unDualNonEmptyMonad . f@@ -934,11 +922,10 @@  deriving (Functor, Show, Eq)  instance (ListMonad m) => Applicative (IdXList m) where-  pure  = return-  (<*>) = ap+  pure x = IdXList x (pure x)+  (<*>)  = ap  instance (ListMonad m) => Monad (IdXList m) where-  return x          = IdXList x (return x)   IdXList x m >>= f = IdXList (componentId $ f x) (m >>= componentM . f)  instance (ListMonad m) => IsNonEmpty (IdXList m a) where@@ -1010,11 +997,10 @@  deriving (Functor, Show, Eq)  instance (HasShortFront m, KnownNat p) => Applicative (ShortFront m p) where-  pure  = return+  pure  = ShortFront . return   (<*>) = ap  instance (HasShortFront m, KnownNat p) => Monad (ShortFront m p) where-  return = ShortFront . return   ShortFront m >>= f | isSingle (unwrap m)                      || nonEmptyAll isSingle                           (unwrap $ unwrap . unShortFront . f <$> m)@@ -1075,14 +1061,13 @@  deriving (Functor, Show, Eq)  instance (HasShortRear m, KnownNat p) => Applicative (ShortRear m p) where-  pure  = return+  pure  = ShortRear . pure   (<*>) = ap  nonEmptyTakeRear :: Int -> NonEmpty a -> [a] nonEmptyTakeRear p = reverse . NonEmpty.take p . NonEmpty.reverse  instance (HasShortRear m, KnownNat p) => Monad (ShortRear m p) where-  return = ShortRear . return   ShortRear m >>= f | isSingle (unwrap m)                     || nonEmptyAll isSingle                          (unwrap $ unwrap . unShortRear . f <$> m)
test/Control/Monad/List/ExoticSpec.hs view
@@ -23,6 +23,7 @@ import Data.Proxy import GHC.Exts (IsList(..)) +deriving instance (Arbitrary (m a)) => Arbitrary (DualListMonad m a) deriving instance (Arbitrary a) => Arbitrary (GlobalFailure a) deriving instance (Arbitrary a) => Arbitrary (MazeWalk a) deriving instance (Arbitrary a) => Arbitrary (DiscreteHybrid a)@@ -98,7 +99,8 @@     it "knows that last of non-empty is non-empty" $       safeLast "Roy" `shouldBe` "y" -  testMonad  "GlobalFailure"      (Proxy :: Proxy GlobalFailure)+  testMonad  "GlobalFailure"  (Proxy :: Proxy GlobalFailure)+  testMonad  "DualListMonad GlobalFailure" (Proxy :: Proxy (DualListMonad GlobalFailure))   describe  "GlobalFailure is ZeroSemigroup" $ do     it                                             "x <> eps       ==  eps"       $ property $ \(x :: GlobalFailure Int)     -> x <> eps       ==  eps@@ -106,7 +108,9 @@       $ property $ \(x :: GlobalFailure Int)     -> eps <> x       ==  eps     it                                             "(x <> y) <> z  ==  x <> (y <> z)"       $ property $ \(x :: GlobalFailure Int) y z -> (x <> y) <> z  ==  x <> (y <> z)-  testMonad  "MazeWalk"           (Proxy :: Proxy MazeWalk)+      +  testMonad  "MazeWalk" (Proxy :: Proxy MazeWalk)+  testMonad  "DualListMonad MazeWalk" (Proxy :: Proxy (DualListMonad MazeWalk))   describe  "MazeWalk is PalindromeAlgebra" $ do     it                                        "x <> eps       ==  eps"       $ property $ \(x :: MazeWalk Int)     -> x <> eps       ==  eps@@ -114,7 +118,9 @@       $ property $ \(x :: MazeWalk Int)     -> eps <> x       ==  eps     it                                        "(x <> y) <> z  ==  x <> (y <> (x <> z))"       $ property $ \(x :: MazeWalk Int) y z -> (x <> y) <> z  ==  x <> (y <> (x <> z))-  testMonad  "DiscreteHybrid"     (Proxy :: Proxy DiscreteHybrid)+      +  testMonad  "DiscreteHybrid" (Proxy :: Proxy DiscreteHybrid)+  testMonad  "DualListMonad DiscreteHybrid" (Proxy :: Proxy (DualListMonad DiscreteHybrid))   describe  "DiscreteHybrid is LeaningAlgebra" $ do     it                                              "x <> eps       ==  eps"       $ property $ \(x :: DiscreteHybrid Int)     -> x <> eps       ==  eps@@ -122,7 +128,9 @@       $ property $ \(x :: DiscreteHybrid Int)     -> eps <> x       ==  x     it                                              "(x <> y) <> z  ==  y <> z"       $ property $ \(x :: DiscreteHybrid Int) y z -> (x <> y) <> z  ==  y <> z-  testMonad  "ListUnfold"         (Proxy :: Proxy ListUnfold)+  +  testMonad  "ListUnfold" (Proxy :: Proxy ListUnfold)+  testMonad  "DualListMonad ListUnfold" (Proxy :: Proxy (DualListMonad ListUnfold))   describe  "ListUnfold is SkewedAlgebra" $ do     it                                          "x <> eps       ==  eps"       $ property $ \(x :: ListUnfold Int)     -> x <> eps       ==  eps@@ -131,10 +139,15 @@     it                                          "(x <> y) <> z  ==  eps"       $ property $ \(x :: ListUnfold Int) y z -> (x <> y) <> z  ==  eps       -  testMonad  "Stutter 1"          (Proxy :: Proxy (Stutter 0))-  testMonad  "Stutter 2"          (Proxy :: Proxy (Stutter 1))+  testMonad  "Stutter 1"          (Proxy :: Proxy (Stutter 1))+  testMonad  "Stutter 2"          (Proxy :: Proxy (Stutter 2))   testMonad  "Stutter 5"          (Proxy :: Proxy (Stutter 5)) +  testMonad  "DualListMonad (Stutter 0)" (Proxy :: Proxy (DualListMonad (Stutter 0)))+  testMonad  "DualListMonad (Stutter 1)" (Proxy :: Proxy (DualListMonad (Stutter 1)))+  testMonad  "DualListMonad (Stutter 2)" (Proxy :: Proxy (DualListMonad (Stutter 2)))+  testMonad  "DualListMonad (Stutter 5)" (Proxy :: Proxy (DualListMonad (Stutter 5)))+   testMonad  "StutterKeeper 0"    (Proxy :: Proxy (StutterKeeper 0))   testMonad  "StutterKeeper 1"    (Proxy :: Proxy (StutterKeeper 1))   testMonad  "StutterKeeper 2"    (Proxy :: Proxy (StutterKeeper 2))@@ -142,6 +155,11 @@   testMonad  "StutterKeeper 4"    (Proxy :: Proxy (StutterKeeper 4))   testMonad  "StutterKeeper 5"    (Proxy :: Proxy (StutterKeeper 5))   testMonad  "StutterKeeper 10"   (Proxy :: Proxy (StutterKeeper 10))++  testMonad  "DualListMonad (StutterKeeper 0)" (Proxy :: Proxy (DualListMonad (StutterKeeper 0)))+  testMonad  "DualListMonad (StutterKeeper 1)" (Proxy :: Proxy (DualListMonad (StutterKeeper 1)))+  testMonad  "DualListMonad (StutterKeeper 2)" (Proxy :: Proxy (DualListMonad (StutterKeeper 2)))+  testMonad  "DualListMonad (StutterKeeper 5)" (Proxy :: Proxy (DualListMonad (StutterKeeper 5)))      testMonad  "StutterStutter 0 0" (Proxy :: Proxy (StutterStutter 0 0))   testMonad  "StutterStutter 0 1" (Proxy :: Proxy (StutterStutter 0 1))@@ -192,9 +210,16 @@    testMonad  "ShortStutterKeeper 0 0" (Proxy :: Proxy (ShortStutterKeeper 0 0))   testMonad  "ShortStutterKeeper 0 1" (Proxy :: Proxy (ShortStutterKeeper 0 1))-  testMonad  "ShortStutterKeeper 0 1" (Proxy :: Proxy (ShortStutterKeeper 1 0))+  testMonad  "ShortStutterKeeper 1 0" (Proxy :: Proxy (ShortStutterKeeper 1 0))   testMonad  "ShortStutterKeeper 1 1" (Proxy :: Proxy (ShortStutterKeeper 1 1))   testMonad  "ShortStutterKeeper 5 3" (Proxy :: Proxy (ShortStutterKeeper 5 3))   testMonad  "ShortStutterKeeper 3 5" (Proxy :: Proxy (ShortStutterKeeper 3 5))++  testMonad  "DualListMonad (ShortStutterKeeper 0 0)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 0 0)))+  testMonad  "DualListMonad (ShortStutterKeeper 0 1)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 0 1)))+  testMonad  "DualListMonad (ShortStutterKeeper 1 0)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 1 0)))+  testMonad  "DualListMonad (ShortStutterKeeper 1 1)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 1 1)))+  testMonad  "DualListMonad (ShortStutterKeeper 5 3)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 5 3)))+  testMonad  "DualListMonad (ShortStutterKeeper 3 5)" (Proxy :: Proxy (DualListMonad (ShortStutterKeeper 3 5)))