0

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?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Bercovici Adrian
  • 8,794
  • 17
  • 73
  • 152
  • 3
    `changeEnvName` should not have type `State Env->State Env`. As said before, a `State s` describes *changing* state (type `s`), not the state itself. – Willem Van Onsem May 23 '19 at 09:27
  • I am not sure about the signatures of the mentioned methods.I thought that `Env`represents the `s` in the `State s a` – Bercovici Adrian May 23 '19 at 09:30
  • The type for `getEnvFileLengths` has one argument but your definition has two. It should have type `State Env a -> State Env [Int]`. You will probably find this easier if you add `readState` and `modifyState` functions. – Lee May 23 '19 at 09:31
  • 2
    As a thumb rule, stick to functions of the form `foo :: type1 -> ... -> typen -> State Env typek`. In most cases you can't write a function `State Env typek -> typem`, like your `readEnv::State a Env->Env`. Make that `readEnv::State Env Env` instead, defined as `readEnv = get`. – chi May 23 '19 at 09:50
  • 2
    The `Env` is probably state, not the "output" value. – Willem Van Onsem May 23 '19 at 10:44
  • @Lee and @chi : I do not know how i should implement the `get` or `read` and `modify state` .I can't figure out what helpers i need in order to use my implementation of the state monad.I am sorry if my question seems too broad but i have looked on tutorials and couldn't understand a basic implementation. – Bercovici Adrian May 23 '19 at 11:16
  • 1
    `changeEnvName` can simply have type `State Env a` - although I presume you want to be able to specify the new name, in which case it has type `String -> State Env a`. To implement it you can just use the `put` method which is pre-defined - although it's not too hard to write your own implementation. – Robin Zigmond May 23 '19 at 11:37
  • @Robin Zigmond : This is what i am trying to do , to understand how to implement the monad , and what other helper methods i need in order to use it having this use case.More specifically i do not understand how to extract ffrom a `s->(a,s)` the `s` from the tuple.All i can think of is : `get st@(State f)=snd . run st ` – Bercovici Adrian May 23 '19 at 11:38
  • 1
    Try having a read of [this](https://en.m.wikibooks.org/wiki/Haskell/Understanding_monads/State) - amongst many other good sources. It sounds like `execState` is what you're after here. – Robin Zigmond May 23 '19 at 11:46
  • 1
    Note that in order to have a tuple to extract from, you always need an "initial state" in order to feed to the function wrapped by the State monad. – Robin Zigmond May 23 '19 at 11:48
  • I have an initial state which is provided by the method `initEnv`.I want to start from that state and then either read from it or change it. – Bercovici Adrian May 23 '19 at 12:51
  • 1
    @BercoviciAdrian `get` is provided by the library, you don't have to implement it. You should probably read some tutorial: looking at examples can make this easier. – chi May 23 '19 at 13:10
  • I have looked at the tutorials but most of them started with complicated examples (for me ) ...i wanted to implement all of this methods for my own example , to finally get it once and for all ,but i am still confused. – Bercovici Adrian May 23 '19 at 13:44

1 Answers1

1

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)
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • So looking at the first method i would need to just call my `initEnv`, but still get rid of the `IO` monad somehow and fill the initial state: `readEnv =State $ \_ -> initEnv>>=\env ->( env,env)` – Bercovici Adrian May 23 '19 at 14:02
  • 2
    @BercoviciAdrian At no point should the implementation of any of these values mention your `initEnv`. It is the caller's responsibility to provide you with an initial environment, and so the caller's responsibility to use `initEnv` somehow. – Daniel Wagner May 23 '19 at 14:08