Reputation: 3
type InterpreterMonad = StateT (Env, Env) (ErrorT String IO ) ()
interpreter :: Stmts -> InterpreterMonad
interpreter (Statements s EmptyStmts) = interpreteStmt s
interpreter (Statements s stmts) = interpreteStmt s >>= \m -> (interpreter stmts)
-- definicja funkcji
interpreteStmt :: Stmt -> InterpreterMonad
interpreteStmt (DefFun (VarName name) args typ begin statements end) = get >>=
\(envOut, (sInnerEnvFun, sInnerEnvEVal)) -> case (Map.lookup (VarName name) sInnerEnvFun) of
Nothing -> put ( ( envOut, ((Map.insert (VarName name) (DefFun (VarName name) args typ begin statements end) sInnerEnvFun), sInnerEnvEVal)) ) >>= \_ -> return ()
(Just x) -> throwError "ee"
Hi, I cannot understand why get
and put
functions can be called? How the Haskell know "where" is state? I have a problem in favour of imperative programming- after all, such function we have to call on object ( as method) or pass state by argument.
Upvotes: 0
Views: 146
Reputation: 30237
The State
type (which StateT
is a generalization of) is implemented something like this:
newtype State s a =
State { -- A function that takes a state of type `s` and
-- produces a pair `(a, s)` of a result of type
-- `a` and a new state of type `s`.
runState :: s -> (a, s)
}
This type has instances for Functor
, Applicative
and Monad
that allow you to assemble complex State
values out of simpler ones:
-- I won't write the code for these here
instance Functor (State s) where ...
instance Applicative (State s) where ...
instance Monad (State s) where ...
Basically, State
is a shortcut for chaining functions of types that look like s -> (a, s)
, but without having to pass around those s
values explicitly. When you're ready to actually "feed" a State
action with an s
value you use one of these operations (depending on which part of the result you want):
runState :: State s a -> s -> (a, s)
evalState :: State s a -> s -> a
execState :: State s a -> s -> s
What put
and get
do, then, is interact with the "hidden" state argument that the State
type implicitly passes around in your code. Their implementations are something like this:
get :: State s s
get = State (\s -> (s, s))
put :: s -> State s ()
put s = State (\_ -> ((), s))
And that's it!
Upvotes: 1
Reputation: 15605
When you use the State
monad functions get
and put
and sequence them with do
notation or >>=
, you are constructing a "recipe" (often called an "action") for accessing and modifying local state that will eventually be used with some particular state. For example:
increment = do
count <- get
put (count + 1)
This action can then be used with the part of the State
interface that lets you run actions by passing in a state. We will use execState
, which returns the state and discards the other value, to demonstrate:
> execState increment 1
2
execState
is what connects the action increment
with the state it modifies. increment
by itself doesn't contain any state, it is just a recipe for interacting with state that you must evaluate later.
Upvotes: 1