I have the following data structure and function:
data BTree a = BLeaf | BNode (BTree a) a (BTree a) deriving (Show, Eq)
freshNodesS :: BTree String -> State [String] (BTree String)
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do l' <- freshNodesS l
let m' = getFresh m s
let s' = m : s
r' <- freshNodesS r s'
return (BNode l' m' r')
There is this problem that I actually want to use the state from freshNodesS l
which should give an output of (BTree String, [String])
, but in a do block I can't use (l', s) <- freshNodesS l
, the only option I see is placing everything in an lambda function.
But is there a way I still can use the do notation?
After what @chi said I did this:
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do l' <- freshNodesS l
m' <- getFreshS m
r' <- freshNodesS r
return (BNode l' m' r')
getFreshS :: String -> State [String] String
getFreshS x = state $ (\s -> let newx = getFresh x s in (newx, newx: s))
And that worked.
The whole point of the State
monad is to automatically pass states around, without you having to do that explicitly. Almost no function should get s
as argument, or return it.
For instance,
let m' = getFresh m s
is suspicious, it should probably read
m' <- getFresh m
where we would have getFresh :: String -> State [String] String
.
The whole code should then read as
do l' <- freshNodesS l
m' <- getFresh m
r' <- freshNodesS r
return (BNode l' m' r')
Note how no s
or s'
is ever mentioned. This should look like imperative code, where a mutable state variable is modified by each function call, even if the code does not mention that explicitly.
Now, in getFresh
you will have to deal with the states, since there's no way around that. If your State
monad is the standard one, you can access the state with get
and put
. You probably need something like
getFresh :: String -> State [String] String
getFresh m = do
s <- get -- read the current state
let m' = ... -- compute a fresh name m'
let s' = m' : s -- mark m' as used
put s' -- write the current state
return m'