%if False > module Let3 where > import Lexer > import Maybe > import Reader > import Monad %endif We need some additional helper functions for accessing and extending environments > type Binding = (String, Int) > > type Result = Reader [Binding] > > extend :: Binding -> Result a -> Result a > extend b m = getenv >>= \ env -> withenv (b : env) m > > access :: String -> Result Int > access s = getenv >>= \ env -> return (fromMaybe 0 (lookup s env)) %if False > %{ > > Terminal = Numeral {Int} > | Ident {String} > | Addop {Op} > | Mulop {Op} > | KWLet as "let" > | KWIn as "in" > | Equal as "=" > | LParen as "(" > | RParen as ")"; > > left 6 Addop {Op}; > left 7 Mulop {Op}; > nonassoc 0 "in"; %endif The following grammar implements the desired evaluator. > expr {Result Int}; > expr {do {b <- d; extend b m}} : "let", decl {d}, "in", expr {m}; > {liftM2 (app op) m1 m2} | expr {m1}, Addop {op}, expr {m2}; > {liftM2 (app op) m1 m2} | expr {m1}, Mulop {op}, expr {m2}; > {return n} | Numeral {n}; > {access s} | Ident {s}; > {m} | "(", expr {m}, ")"; > > decl {Result Binding}; > decl {do {v <- m; return (s,v)}} : Ident {s}, "=", expr {m}; %if False > }% > > frown _ = fail "syntax error" %endif Note that there are two monads around: the parsing monad (in fact, |expr| is parametric in this monad) and the reader monad, which is embedded in the attributes. The parser returns a value of type |Reader Int|, to which we pass an empty initial environment. > eval :: (Monad m) => [Char] -> m Int > eval inp = do {f <- expr (lexer inp); return (apply f [])}