Add instance Monad ZipList

Roman Cheplyaka roma at ro-che.info
Thu Jun 4 20:04:25 UTC 2020


On 04/06/2020 09.53, Dannyu NDos wrote:
> instance Monad ZipList where
>     ZipList [] >>= _ = ZipList []
>     ZipList (x:xs) >>= f = ZipList $ do
>         let ZipList y' = f x
>         guard (not (null y'))
>         let ZipList ys = ZipList xs >>= ZipList . join . maybeToList . fmap snd . uncons . getZipList . f
>         head y' : ys
> 
> instance MonadFail ZipList where
>     fail _ = empty
> 
> instance MonadPlus ZipList

While others have commented on the general feasibility of the idea, the problem with this specific instance is that it appears to violate the associativity law:

% ./ziplist --smallcheck-depth=3                                                           
Monad laws
  Right identity: OK
    21 tests completed
  Left identity:  OK
    98 tests completed
  Associativity:  FAIL (0.04s)
    there exist {True->ZipList {getZipList = [True]};False->ZipList {getZipList = [False,True]}} {True->ZipList {getZipList = [True,True]};False->ZipList {getZipList = []}} ZipList {getZipList = [True,False]} such that
      condition is false

1 out of 3 tests failed (0.05s)


Here's the code I used for testing:

{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses #-}
import Control.Applicative
import Control.Monad
import Data.List
import Data.Maybe
import Test.SmallCheck.Series
import Test.Tasty
import Test.Tasty.SmallCheck

instance Monad ZipList where
    ZipList [] >>= _ = ZipList []
    ZipList (x:xs) >>= f = ZipList $ do
        let ZipList y' = f x
        guard (not (null y'))
        let ZipList ys = ZipList xs >>= ZipList . join . maybeToList . fmap snd . uncons . getZipList . f
        head y' : ys

instance Serial m a => Serial m (ZipList a) where
  series = ZipList <$> series

main = defaultMain $ testGroup "Monad laws"
  [ testProperty "Right identity" $ \(z :: ZipList Int) ->
      (z >>= return) == z
  , testProperty "Left identity" $ \(b :: Bool) (f :: Bool -> ZipList Bool) ->
      (return b >>= f) == f b
  , testProperty "Associativity" $
      \(f1 :: Bool -> ZipList Bool)
       (f2 :: Bool -> ZipList Bool)
       (z :: ZipList Bool) ->
         (z >>= (\x -> f1 x >>= f2)) == ((z >>= f1) >>= f2)
  ]

Roman


More information about the Libraries mailing list