Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making normal monadic functions work with the monad transformer equivalent

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:

  1. No matter what I do I can't find a way to apply push and pop to the Stack contained in the StateT.
  2. I have no idea how to call this from the main function

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
like image 313
TheIronKnuckle Avatar asked Feb 08 '12 06:02

TheIronKnuckle


People also ask

Is every monad Transformer a monad?

All monad transformers are instances of MonadTrans , and so lift is available for them all. There is a variant of lift specific to IO operations, called liftIO , which is the single method of the MonadIO class in Control. Monad. IO.

What is a monadic function?

A monadic function is a function with a single argument, written to its right. It is one of three possible function valences; the other two are dyadic and niladic. The term prefix function is used outside of APL to describe APL's monadic function syntax.

How does a monad work?

What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

What is liftIO?

liftIO allows us to lift an IO action into a transformer stack that is built on top of IO and it works no matter how deeply nested the stack is.


1 Answers

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.

like image 156
shang Avatar answered Sep 28 '22 16:09

shang