haskell - Why must we use state monad instead of passing state directly? -


can show simple example state monad can better passing state directly?

bar1 (foo x) = foo (x + 1) 

vs

bar2 :: state foo foo bar2 =   modify (\(foo x) -> foo (x + 1))   

state passing tedious, error-prone, , hinders refactoring. example, try labeling binary tree or rose tree in postorder:

data rosetree = node [rosetree a] deriving (show)  postlabel :: rosetree -> rosetree int postlabel = fst . go 0   go (node _ ts) = (node i' ts', i' + 1)      (ts', i') = gots ts      gots []     = ([], i)     gots (t:ts) = (t':ts', i'')       (t', i')   = go t       (ts', i'') = gots i' ts 

here had manually label states in right order, pass correct states along, , had make sure both labels , child nodes in right order in result (note naive use of foldr or foldl child nodes have led incorrect behavior).

also, if try change code preorder, have make changes easy wrong:

prelabel :: rosetree -> rosetree int prelabel = fst . go 0   go (node _ ts) = (node ts', i') -- first change      (ts', i') = gots (i + 1) ts -- second change      gots []     = ([], i)     gots (t:ts) = (t':ts', i'')       (t', i')   = go t       (ts', i'') = gots i' ts 

examples:

branch = node () nil  = branch [] tree = branch [branch [nil, nil], nil] prelabel tree == node 0 [node 1 [node 2 [],node 3 []],node 4 []] postlabel tree == node 4 [node 2 [node 0 [],node 1 []],node 3 []] 

contrast state monad solution:

import control.monad.state import control.applicative  postlabel' :: rosetree -> rosetree int postlabel' = (`evalstate` 0) . go   go (node _ ts) =     ts' <- traverse go ts       <- <* modify (+1)     pure (node ts')  prelabel' :: rosetree -> rosetree int prelabel' = (`evalstate` 0) . go   go (node _ ts) =       <- <* modify (+1)     ts' <- traverse go ts     pure (node ts') 

not code more succinct , easier write correctly, logic results in pre- or postorder labeling far more transparent.


ps: bonus applicative style:

postlabel' :: rosetree -> rosetree int postlabel' = (`evalstate` 0) . go   go (node _ ts) =     flip node <$> traverse go ts <*> (get <* modify (+1))  prelabel' :: rosetree -> rosetree int prelabel' = (`evalstate` 0) . go   go (node _ ts) =     node <$> (get <* modify (+1)) <*> traverse go ts 

Comments