Search code examples
haskellstate-monad

How to use the State Monad


I have already asked a question about understanding the state monad , but now i felt this one should be a different one.

Given i implemented the state monad, i want my state to be a data structure that resembles the Environment:

Environment

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}

I do not know how to integrate this data structure as the State in the State monad in order to be able to change the name of the environment , print it or use it . It might seem too broad but i cannot grasp it without a full example:

State monad implementation

newtype State s a = State {run::s->(a,s)} 

instance Functor (State s) where
    fmap=Control.Monad.liftM
instance Applicative (State s) where
    pure=return
    (<*>)=Control.Monad.ap
instance Monad (State s) where
    return a= State $ \k->(a,k) 
    (>>=) m f=State $ \s -> let (a,s')=run m s in
        run (f a) s'

What i want to implement

readEnv::State a Env->Env
readEnv m =

changeEnvName::State a Env->State a Env
changeEnvName m =  --given the environment packed in a state , 
                   -- i want to change the name 

getEnvFileLengths::State a Env->[Int]
getEnvFileLengths s a= s>>= getLengths

getLengths::[String]->[Int]
getLengths xs=map length xs

P.S I understand that i should use Reader or Writer monad but i wanted a all in one approach to be able to understand how all things fit together.

Any ideas?


Solution

  • It might be easier to make progress if you get the type signatures right:

    readEnv::State Env Env
    changeEnvName::String -> State Env ()
    getEnvFileLengths::State Env [Int]
    

    If those look like strange choices of types to you, it might be worth trying to expand away the newtype and see if they appear more sensible after that:

    -- give me an initial environment from your store, I'll give you the new environment
    -- to store and another copy of the environment as the result of the computation
    readEnv :: Env -> (Env, Env)
    
    -- give me a new name and the old environment, I'll give you a new environment and
    -- a trivial acknowledgement that I'm done
    changeEnvName :: String -> Env -> ((), Env)
    
    -- give me an initial environment that you're storing, I'll give you the new
    -- environment to store (actually same as the old one) and the result of the
    -- length computations
    getEnvFileLengths :: Env -> ([Int], Env)