R. Bosman
R. Bosman

Reputation: 188

Haskell State monad: get updated state after sequence

I'm building a C# compiler in haskell as part of a compilers course. I am using the state monad, and the problem lies in the code for the block. I'm using the state to wrap the env of declared vars. When parsing a block, I want to extend this state (because of declarations inside the block), but afterwards return to the original block (since declerations don't go outside the block). However, I want to know the size of the new updated state first. So I have the code below:

type EnvState = State Env (Int, Code)
type Env = M.Map String Int

fStatBlock :: [EnvState] -> EnvState
fStatBlock block = do origEnv <- get
                      xs      <- sequence block -- prelude sequence
                      newEnv  <- get
                      put origEnv
                      return (M.size newEnv, concatMap snd xs)

The env is of type Data.Map.

My problem is that the newEnv isn't the updated env after sequence, but equal to the origEnv. Therefore, the size returned is 100% dependent on the size of the original env, and does not change no matter what is inserted in the sequence. (I have tested the insert method and it works).

Is this somehow due to lazy evaluation? Weird execution order? Or should this give the new, updated env and am I doing something wrong somewhere else? Thanks for the help.

Upvotes: 2

Views: 871

Answers (1)

Dave Compton
Dave Compton

Reputation: 1423

As Justin L. suggests, the problem probably is that block is not changing the Env. Here's your example code, unchanged, with some test code added to make a complete program. Function setValue produces an EnvState that changes the Env. Using setValue in the call to fStatBlock does result in a size that is one greater than it would be if based on the original env.

complete program

import Control.Monad.State 
import Data.Map as M

type Code = [Int]


-- begin original code ------

type EnvState = State Env (Int, Code)
type Env = M.Map String Int

fStatBlock :: [EnvState] -> EnvState
fStatBlock block = do origEnv <- get
                      xs      <- sequence block -- prelude sequence
                      newEnv  <- get
                      put origEnv
                      return (M.size newEnv, concatMap snd xs)

-- end original code -------


setValue :: String -> Int -> EnvState
setValue name value = state (\env -> ((0,[]), insert name value env))

main = do
    let env = fromList [("x", 5), ("y", 10)]
        fsb = fStatBlock [setValue "a" 15] 
    print $ fst $ fst $ runState fsb env

results

$ ./main 
3

Upvotes: 2

Related Questions