Reputation: 521
I've gotten stuck on the following predicament.
evalAExpr :: AExpr -> Env -> Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
where
actAccordingly (Left int) = int
actAccordingly (Right stmt) = eval stmt env
eval :: Stmt -> Env -> IO Env`
getValueOfBinding :: Env -> String -> Either Integer Stmt
So what needs to happen is that with the Stmt x
gotten from the Either Monad, it should evaluate x
(which needs to happen in an IO
, because of other processes) into a new Environment, after which I can return the correct value from the environment. However, from my limited knowledge of Haskell I can't ever get out of an IO
.
Eg: givetheCorrectThing nameOfTheValueInTheEnv (eval stmt env)
would never work with the current code, since all evalAExpr
functions (yes there are more than just 1) return an Env atm. If I would have to change that, it would result in adding in ALL of the evaluation do and return statements. > 50-60 occurences. For sure. That would make code so messy to look at. The reason I need IO is because this evaluation will have to go (at some point) and collect data from an mBot (http://makeblock.com/mbot-stem-educational-robot-kit-for-kids/), aswell as collect data from the keyboard (done via getLine)
Would any of you have a way to rework this code so that I can keep it neat and organised?
If more code is needed: Feel free to ping / mention me. I will provide the requested lines of code.
More code as requested:
-- Code that shows how EvalAExpr is used.
-- Relational operators
evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env
evalBExpr (RBinary Less a b) env = evalAExpr a env < evalAExpr b env
evalBExpr (RBinary Equal a b) env = evalAExpr a env == evalAExpr b env
Upvotes: 1
Views: 175
Reputation: 120741
from my limited knowledge of Haskell I can't ever get out of an IO
Correct†! And for good reasons: with the current signature, anybody using your function knows that this really is just a pure, referentially transparent calculation, so they can be accordingly careless with it (lazyness etc.). If you were allowed to just put in some IO
without making this explicit through the signature, you might easily break everybody's code!
Of course, if you've only just noticed that you need to do IO, then you are indeed in a bit of a dilemma. There's no good way for you to get around changing all the signatures now.
However, you could have prevented this by using a type synonym‡ in the first place: if you'd had originally
type Evaluation a = Identity a
-- ... lots of functions with an `Evaluation` result ...
evalAExpr :: AExpr -> Env -> Evaluation Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
where
actAccordingly (Left int) = return int
actAccordingly (Right stmt) = someThingThatDidn'tYetRequireIO
then, upon noticing that you need IO
after all, you could have just changed it to
type Evaluation a = IO a
{- same as before:
evalAExpr :: AExpr -> Env -> Evaluation Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
where
actAccordingly (Left int) = return int -}
actAccordingly (Right stmt) = eval stmt env
In a big project, the Evaluation
monad would then typically be a monad transformer stack, to which you'd just add an extra layer for the IO underneath.
†If anybody reminds me about the function that shall not be named, I shall whack them about the head with a nuclear missile.
‡As dfeuer remarks, a newtype
may be better.
Upvotes: 2
Reputation: 52049
You have to write a monadic version of evalAExpr
We'll call the new version evalAExprIO
:
evalAExprIO :: AExpr -> Env -> IO Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
where
actAccordingly (Left int) = return int
actAccordingly (Right stmt) = eval stmt env
There is really only one significant change -- in the Left
case the
function actAccordingly
uses return int
instead of just int
.
The type signature for evalAExprIO
has changed, but this is something
that GHC can infer so it's not something you have to know how to change.
Update
Change:
evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env
to:
evalBexpr (RBinary Greater a b) env = liftM2 (>) (evalAExpr a env) (evalExpr b env)
(import Control.Monad for liftM2.)
Upvotes: 1