So I'm making my own primitive shell language parser and I thought I would make my commands (such as cd) expressions so that I can leave some space for them to return values.
Now I have made some kind of parser and I have a naked expression in my statement consumer.
I hope for your sake that I do not have to provide you with the whole code. This is the problematic part:
-- it has other patterns...
makeStatement :: Statement -> VarTable -> Maybe VarTable
makeStatement (Exp exp) vt = ???
makeStatement (If ..........
...
data Expression = Val Int
| Booly Bool
| Var String
| Cmd String [String]
deriving (Show)
data Statement = Assignment String Expression
| If Expression Statement
| IfElse Expression Statement Statement
| Exp Expression
deriving (Show)
eval :: VarTable -> Expression -> Maybe Int
So I want to force execution of exp in case its a Cmd
but return the same vt it began with.
Firstly, you're probably going to need some composite type to represent the three possible types your expressions can return. Something like
data Value = Number Int | Boolean Bool | String String | Unit
The Unit
one is for expressions that don't return anything.
Now, since some of your expressions might have side-effects, you're definitely going to have to wrap everything up into IO. It doesn't matter that there are pure expressions because the compiler doesn't know which one is which.
eval :: VarTable -> Expression -> IO Value
Now your makeStatement
(something like runStatement
might be a better name) would look like this
makeStatement :: Statement -> VarTable -> IO VarTable
makeStatement (Exp exp) vt = eval exp >> return vt
makeStatement (If cond th) vt = do
c <- eval cond
case c of
Boolean True -> makeStatement th vt
_ -> return vt --maybe handle the case where c isn't even Boolean
...
With this setup you can see that we can do what I'm guessing you wanted. eval
uate the expression, and then discard it's result and just return the VarTable
unchanged.