Your use of the battery makes the conclusion unnecessarily strict. Batteries are good in strict languages ββbecause they allow tail recursion; in lazy languages ββthey are not needed (and often bad). I rewrote your code below so as not to use it.
module Interpreter where -------------------------------------------------------------------- type Id = [Char] type Output = [Char] type Value = Int type Table = [(Id, Value)] data Stm = CompoundStm Stm Stm | AssignStm Id Exp | PrintStm ExpList deriving Show data Exp = IdExp Id | NumExp Value | OpExp Exp Op Exp | EseqExp Stm Exp deriving Show data ExpList = PairExpList Exp ExpList | LastExpList Exp deriving Show data Op = Plus | Minus | Times | Div deriving Show example :: Stm example = CompoundStm (AssignStm "a" (OpExp (NumExp 5) Plus (NumExp 3))) (CompoundStm (AssignStm "b" (EseqExp (PrintStm (PairExpList (IdExp "a") (LastExpList (OpExp (IdExp "a") Minus (NumExp 1))))) (OpExp (NumExp 10) Times (IdExp "a")))) (PrintStm (LastExpList (IdExp "b")))) -------------------------------------------------------------------- tableUpdate :: Table -> Id -> Value -> Table tableUpdate tiv = (i,v):t tableLookup :: Table -> Id -> Value tableLookup ((x,v):t) i | x == i = v tableLookup ((x,v):t) i | x /= i = tableLookup ti -------------------------------------------------------------------- execute :: Stm -> IO() execute s = putStr ((\(o,_)->o) (interpStm s [])) interpStm :: Stm -> Table -> (Output, Table) interpStm (CompoundStm lr) t = let (o, t') = interpStm lt in let (o', t'') = interpStm rt' in (o ++ o', t'') interpStm (PrintStm el) t = interpExpList el t interpStm (AssignStm ie) t = let (v, o, t') = interpExp et in (o, tableUpdate t' iv) interpExp :: Exp -> Table -> (Value, Output, Table) interpExp (IdExp i) t = (tableLookup ti, "", t) interpExp (NumExp v) t = (v, "", t) interpExp (EseqExp se) t = let (o, t') = interpStm st in let (v, o', t'') = interpExp et' in (v, o ++ o', t'') interpExp (OpExp l op r) t = let (v, o, t') = interpExp lt in let (v', o', t'') = interpExp rt' in (gv op v', o++o', t'') where g v1 Plus v2 = v1 + v2 g v1 Minus v2 = v1 - v2 g v1 Times v2 = v1 * v2 g v1 Div v2 = v1 `div` v2 interpExpList :: ExpList -> Table -> (Output, Table) interpExpList (LastExpList e) t = let (v, o, t') = interpExp et in (o ++ show v ++ "\n", t') interpExpList (PairExpList e el) t = let (v, o, t') = interpExp et in let (o', t'') = interpExpList el t' in (o ++ show v ++ " " ++ o', t')
With this change, the conclusion is lazy.
You will notice that a lot of repeating form code let (value, newTable) = f oldTable in ... and a lot of repeating form code let (output, value) = exp; (moreOutput, value) = exp2 in (output ++ moreOutput, exp3) let (output, value) = exp; (moreOutput, value) = exp2 in (output ++ moreOutput, exp3) . There are a couple of monads who write this code for you! Here is an example of using the StateT table (Writer output):
module Interpreter where import Control.Monad.Writer import Control.Monad.State import Data.Maybe -------------------------------------------------------------------- type Id = [Char] type Output = [Char] type Value = Int type Table = [(Id, Value)] data Stm = CompoundStm Stm Stm | AssignStm Id Exp | PrintStm ExpList deriving Show data Exp = IdExp Id | NumExp Value | OpExp Exp Op Exp | EseqExp Stm Exp deriving Show data ExpList = PairExpList Exp ExpList | LastExpList Exp deriving Show data Op = Plus | Minus | Times | Div deriving Show type InterpreterM = StateT Table (Writer Output) example :: Stm example = CompoundStm (AssignStm "a" (OpExp (NumExp 5) Plus (NumExp 3))) (CompoundStm (AssignStm "b" (EseqExp (PrintStm (PairExpList (IdExp "a") (LastExpList (OpExp (IdExp "a") Minus (NumExp 1))))) (OpExp (NumExp 10) Times (IdExp "a")))) (PrintStm (LastExpList (IdExp "b")))) -------------------------------------------------------------------- tableUpdate :: Id -> Value -> InterpreterM () tableUpdate iv = modify ((i,v):) tableLookup :: Id -> InterpreterM Value tableLookup i = gets (fromJust . lookup i) -------------------------------------------------------------------- execute :: Stm -> IO () execute s = putStr . execWriter $ evalStateT (interpStm s) [] interpStm :: Stm -> InterpreterM () interpStm (CompoundStm lr) = interpStm l >> interpStm r interpStm (PrintStm el) = interpExpList el interpStm (AssignStm ie) = interpExp e >>= tableUpdate i interpExp :: Exp -> InterpreterM Value interpExp (IdExp i) = tableLookup i interpExp (NumExp v) = return v interpExp (EseqExp se) = interpStm s >> interpExp e interpExp (OpExp l op r) = liftM2 (g op) (interpExp l) (interpExp r) where g Plus v1 v2 = v1 + v2 g Minus v1 v2 = v1 - v2 g Times v1 v2 = v1 * v2 g Div v1 v2 = v1 `div` v2 interpExpList :: ExpList -> InterpreterM () interpExpList (LastExpList e) = interpExp e >>= \v -> tell (show v ++ "\n") interpExpList (PairExpList e el) = interpExp e >>= \v -> tell (show v ++ " ") >> interpExpList el
There are many other changes that can be proposed here, but I hope you agree that this final form is much more pleasant to read than the previous one.