Search code examples
parsinghaskellparsec

How to force execution of a function and then return a simple something else in haskell


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.


Solution

  • 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. evaluate the expression, and then discard it's result and just return the VarTable unchanged.