haskell / mtl

The Monad Transformer Library
http://www.haskell.org/haskellwiki/Monad_Transformers
Other
362 stars 63 forks source link

Control.Monad.State.Class.modify' does not behave like Control.Monad.Trans.State.Lazy.modify' #39

Closed autotaker closed 7 years ago

autotaker commented 7 years ago

Control.Monad.State.Class.modify' is defined as follows.

modify' :: MonadState s m => (s -> s) -> m ()
modify' f = state (\s -> let s' = f s in s' `seq` ((), s'))

According to the instance of MonadState s (StateT s m) , this is equivalent to:

modify' f = state (\s -> let s' = f s in s' `seq` ((), s'))
          = StateT (return . (\s -> let s' = f s in s' `seq` ((), s')))
          = StateT (\s -> return (let s' = f s in s' `seq` ((), s')))

Because (let s' = f s in s' `seq` ((), s')) is encapsulated by return, f s does not evaluated when modify f is evaluated.

I think this behavior is NOT desired. In the transformers package, modify' is defined as follows.

modify' :: (Monad m) => (s -> s) -> StateT s m ()
modify' f = do
    s <- get
    put $! f s

With this definition, modify' f evaluates f s, as expected.

It seems that pull request #13 fixes this issue, but it haven't been merged for a few years.

RyanGlScott commented 7 years ago

Fixed in 56d270389ce1b852e522ce70c93e79660852a753.