Reputation: 7294
I'm trying to solve the balanced brackets problem. I don't want to do continuous IO, and would rather make a single call to getLine and parse the resulting string. Therefore the function that solves the problem will deal with two different states: The unconsumed part of the input string, and the bracket stack.
I want to set up some functions for manipulating a stack:
type Stack = String
pop :: Stack -> (Char,Stack)
pop (x:xs) = (x,xs)
push :: Char -> Stack -> ((),Stack)
push a xs = ((),a:xs)
That's all good if I'm operating in the state monad, however I'm operating in the StateT monad
balanced :: StateT Stack (State String) Bool
I know I've been told not to have duplicate monads in the stack. I'm doing it this way because I like how it simplifies the push and pop definitions.
Two problems:
Here's the rest of the code
next :: String -> (Maybe Char,String)
next "" = (Nothing,[])
next (x:xs) = (Just x,xs)
balanced = do
c <- lift (state next)
case c of
Nothing -> return True
Just c -> if elem c open
then (push c) >> balanced
else if elem c close
then pop >>= \x ->
if eq x c
then balanced
else return False
else balanced
where open = "<{(["
close = "])}>"
eq '(' ')' = True
eq '{' '}' = True
eq '<' '>' = True
eq '[' ']' = True
eq _ _ = False
Upvotes: 5
Views: 226
Reputation: 24802
Your problem is that your push
and pop
are just ordinary, non-monadic function which you are trying to use in the monadic do-block. You are using next
correctly, since you call it using the state
function, but as you probably noticed, state
only works for the plain State
monad and not StateT
.
We can implement a monad transformer version of state
like this:
stateT :: Monad m => (s -> (a, s)) -> StateT s m a
stateT f = do
(x, s') <- gets f
put s'
return x
And then use it in the balanced
function with push
and pop
.
balanced :: StateT Stack (State String) Bool
balanced = do
c <- lift (state next)
case c of
Nothing -> return True
Just c -> if elem c open
then (stateT $ push c) >> balanced
else if elem c close
then stateT pop >>= \x ->
if eq x c
then balanced
else return False
else balanced
where open = "<{(["
close = "])}>"
eq '(' ')' = True
eq '{' '}' = True
eq '<' '>' = True
eq '[' ']' = True
eq _ _ = False
The function is called like this:
evalState (evalStateT balanced []) s
Where s
is the initial string and []
is the initial stack.
Upvotes: 7