Raphaël Mor
Raphaël Mor

Reputation: 356

How to apply stateful computation to a list?

Let's imagine a dummy subset of Brainf*ck:

+ increments the counter

- decrements the counter

A simple program:

program = "++++--" -- should evaluate to 2

And a stateful evaluation function:

eval :: Char -> State Int Char
eval '+' = do x <- get
              put (x + 1)
              return 'I'
eval '-' = do x <- get
              put (x - 1)
              return 'D'

How would you evaluate the program? (Looks like a fold to me but can't get my head around it, and it doesn't feel like it's the way to do it properly...)

Upvotes: 1

Views: 179

Answers (3)

jakubdaniel
jakubdaniel

Reputation: 2223

An ugly solution but using the fold that you correctly suspected was applicable.

import Control.Monad.Trans.State

program = "++++--"

eval :: Char -> State Int Char
eval '+' = do
    x <- get
    put (x + 1)
    return 'I'
eval '-' = do
    x <- get
    put (x - 1)
    return 'D'

evalList :: [Char] -> State Int Char
evalList = foldl (\s c -> (s >> eval c)) (return ' ')

main = putStrLn $ show $ runState (evalList program) 0

Upvotes: 1

Lee
Lee

Reputation: 144126

You can use traverse_ from Data.Foldable:

import Data.Foldable (traverse_)
execState (traverse_ eval "++++--") 0

Upvotes: 7

C. Quilley
C. Quilley

Reputation: 1059

The function you are looking for is sequence, which has the signature sequence :: Monad m => [m a] -> m [a], and is a very common pattern when dealing with monads like State.

For your code, you would expect the evaluator to look like this:

evalBF :: String -> State Int String
evalBF = sequence . map eval

Which you would then fully evaluate with something like:

main :: IO ()
main = do
       src <- getLine
       print $ runState (evalBF src) 0

Upvotes: 5

Related Questions