JosephConrad
JosephConrad

Reputation: 688

Haskell - store variables

I write simple interpreter and I would like to store variables. Up till now I have:

-- MyEnv is a map from strings into integers
type MyEnv = M.Map String Int
type TCM a = ErrorT String (StateT MyEnv IO) a

I have a definition

ms_assgn :: Assgn -> TCM()
ms_assgn (Assgn (Ident ident) exp) = do
    map <- get
    w1 <- ms_exp exp
    put (M.insert (ident w1 map))

and I have obtained the following mistatkes:

Interpret.hs:118:5:
Couldn't match type `Envnt' with `a0 -> M.Map k0 a0 -> M.Map k0 a0'
When using functional dependencies to combine
  MonadState s (StateT s m),
    arising from the dependency `m -> s'
    in the instance declaration in `Control.Monad.State.Class'
  MonadState (a0 -> M.Map k0 a0 -> M.Map k0 a0) (StateT Envnt IO),
    arising from a use of `put' at Interpret.hs:118:5-7
In a stmt of a 'do' block: put (M.insert (ident w1 map))
In the expression:
  do { map <- get;
       w1 <- ms_exp exp;
       put (M.insert (ident w1 map)) }

Interpret.hs:118:20:
Couldn't match expected type `Integer -> Envnt -> k0'
            with actual type `[Char]'
The function `ident' is applied to two arguments,
but its type `String' has none
In the first argument of `M.insert', namely `(ident w1 map)'
In the first argument of `put', namely `(M.insert (ident w1 map))'

when I commented out the last line with put and replace it with return() it makes nothing resonable, but at least it compiles. The ms_assgn function I understand this way:

What is wrong with it? Any hints?

Upvotes: 2

Views: 481

Answers (1)

Dietrich Epp
Dietrich Epp

Reputation: 213758

It's just an extra set of parentheses.

M.insert (ident w1 map) -- wrong

The insert function has type k -> a -> Map k a -> Map k a, but those extra parentheses mean you are calling ident as if it were a function.

M.insert ident w1 map -- correct

However, as a semantic issue, you might run into unexpected behavior if ms_exp exp modifies the environment, because those changes would be lost. I would move that above the modification of the environment:

ms_assgn (Assgn (Ident ident) exp) = do
  w1 <- ms_exp exp
  map <- get
  put $ M.insert ident w1 map

And a get followed by a put can be changed to a modify, currying insert. Incidentally, if you ever wondered why the Map k a is the last argument to insert, this is the reason.

ms_assgn (Assgn (Ident ident) exp) = do
  w1 <- ms_exp exp
  modify $ M.insert ident w1

And if you like, you can identify that the two do lines are really just a single >>=, so...

ms_assgn (Assgn (Ident ident) exp) =
  ms_exp exp >>= modify . M.insert ident

You can see how, rather than using imperative do, the data flows through the monadic bind operator >>= into an action which modifies the environment.

Upvotes: 9

Related Questions