Вот пример немонады, которая не справляется с ассоциативностью:
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad
newtype ListIO a = L { runListIO :: IO [a] }
deriving Functor
instance Applicative ListIO where
pure x = L $ return [x]
(<*>) = ap
instance Monad ListIO where
(L m) >>= f = L $ do
xs <- m
concat <$> mapM (runListIO . f) xs
Если ассоциативность удовлетворена, эти два do
блока будут эквивалентны
act1 :: ListIO Int
act1 = do
L (pure [1,2,3])
do L (putStr "a" >> return [10])
L (putStr "b" >> return [7])
act2 :: ListIO Int
act2 = do
do L (pure [1,2,3])
L (putStr "a" >> return [10])
L (putStr "b" >> return [7])
Однако, запуск Действия выдают разные результаты:
main :: IO ()
main = do
runListIO act1 -- ababab
putStrLn ""
runListIO act2 -- aaabbb
return ()
В общем случае утверждение закона ассоциативности может быть трудным. Конечно, можно написать тесты, но идеальным способом обеспечения ассоциативности было бы написать математическое доказательство закона. В некоторых случаях для доказательства ассоциативности достаточно эквационального мышления. Иногда нам также нужна индукция.