Search code examples
haskellstate-monad

Cannot use state monad in IO context


I am trying to use the State monad to do some computations while also altering it. I have implemented the instances of Applicative, Monad and Functor as well as get and put ,modify, etc. I do not understand the desugaring of the do block. How do you supply both the state and the state transformer?

Utils

    get::State s s
    get=State $ \s ->(s,s) 

    put::s->State s ()
    put x=State $ \_ -> ((),x)

    modify::(s->s)->State s ()
    modify f=get>>= \x -> put (f x)

    evalState::State s a->s->a
    evalState act =fst . run act

    execState::State s a->s->s
    execState act=snd.run act

Code

module Env where
    import State 
    import System.Directory
    import Control.Monad
    data Env=Env{
        envName::String,
        fileNames::[String]
    }
    instance Show Env where 
        show Env{envName=x,fileNames=xs} = "{ envName:"++x++" , files: ["++foldr (\t y-> t++","++y) "" xs ++"] }"

    initEnv::IO Env
    initEnv=do
        name<- getLine
        names<- getCurrentDirectory>>=listDirectory
        return Env{envName=name,fileNames=names}

    changeName::String->State Env ()
    changeName (y:ys)=State $ \ (Env (x:xs) ls) -> ((),Env (y:xs) ls)

    toStats::State Env String
    toStats= State $ \env -> (show env,env)

    useEnv::IO (State Env String)
    useEnv=do
        liftM put initEnv   --passes state transformer
        liftM changeName getLine  --passes strate transformer
        print . evalState . toStats         --how do i supply both ?
        return toStats

As you can see, in my last line I am initializing the state transformer and passing it for further altering... until i reach evalState and I want to use it. In this case i do not know how to supply the state and the transformer.

P.S By transformer i mean the wrapper over the s->(a,s)


Solution

  • Your code doesn't use any monad transformers. I think you're just looking for

    useEnv :: IO ()
    useEnv = do
        env <- initEnv
        name <- getLine
        let actions = do
                changeName name
                toStats
        let str = evalState actions env
        putStrLn str
    

    i.e. run the State actions from inside an IO do block.