Skip to content

Commit

Permalink
Fix dual monads
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejpirog committed Sep 20, 2023
1 parent 3e65ec2 commit 6c59b56
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

- 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
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: exotic-list-monads
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.
version: 1.1.0
version: 1.1.1
license: MIT
copyright: (c) 2020 Dylan McDermott, Maciej Piróg, Tarmo Uustalu
author: Maciej Piróg <maciej.adam.pirog@gmial.com>
Expand Down
14 changes: 5 additions & 9 deletions src/Control/Monad/List/Exotic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,17 @@ liftListFun f = wrap . f . unwrap
-- | 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
-- (where join is the join of the original list monad).
--
-- @
-- reverse . return
-- @
--
-- (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 = DualListMonad . liftListFun reverse . pure
pure = DualListMonad . pure
(<*>) = ap

instance (ListMonad m) => Monad (DualListMonad m) where
Expand Down
10 changes: 3 additions & 7 deletions src/Control/Monad/List/NonEmpty/Exotic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -875,16 +875,12 @@ liftNEFun f = wrap . f . unwrap
-- as
--
-- @
-- join . reverse . fmap reverse
-- reverse . join . reverse . fmap reverse
-- @
--
-- (where join is the join of the original list monad), while return is
-- (where join is the join of the original list monad).
--
-- @
-- reverse . return
-- @
--
-- (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)
Expand Down
39 changes: 32 additions & 7 deletions test/Control/Monad/List/ExoticSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Control.Monad (join)
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)
Expand Down Expand Up @@ -98,31 +99,38 @@ spec = do
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
it "eps <> x == eps"
$ 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
it "eps <> x == eps"
$ 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
it "eps <> x == x"
$ 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
Expand All @@ -131,17 +139,27 @@ spec = do
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))
testMonad "StutterKeeper 3" (Proxy :: Proxy (StutterKeeper 3))
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))
Expand Down Expand Up @@ -192,9 +210,16 @@ spec = do

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)))


0 comments on commit 6c59b56

Please sign in to comment.