Adding definitions
6.4.3 Adding definitions
Our next goal is to extend the evaluator of the previous subsection such that it can handle definitions as well. A definition is an expression of the form Def name expr1 expr2, which should be interpreted as: let the value of name be equal to expr1 in expression expr2. Variables are typically defined by updating the environment with an appropriate name-value pair.
The datatype (called Expr again) and the corresponding algebra type and eval func- tion are now as follows:
data Expr = Expr ‘Add‘ Expr
| Expr ‘Min‘ Expr | Expr ‘Mul‘ Expr | Expr ‘Dvd‘ Expr | Num Value | Var Name | Def Name Expr Expr
type ExprAlgebra a = (a->a->a
-- add
,a->a->a
-- min
,a->a->a
-- mul
,a->a->a
-- dvd
Compositionality
,Value->a
-- num
,Name->a
-- var
,Name->a->a->a) -- def
foldExpr :: ExprAlgebra a -> Expr -> a foldExpr (add,min,mul,dvd,num,var,def) = fold where
fold (expr1 ‘Add‘ expr2) = fold expr1 ‘add‘ fold expr2 fold (expr1 ‘Min‘ expr2) = fold expr1 ‘min‘ fold expr2 fold (expr1 ‘Mul‘ expr2) = fold expr1 ‘mul‘ fold expr2 fold (expr1 ‘Dvd‘ expr2) = fold expr1 ‘dvd‘ fold expr2 fold (Num n)
= num n
fold (Var x)
= var x
fold (Def x value body)
= def x (fold value) (fold body)
Expr now has an extra constructor: the ternary constructor Def, which can be used to introduce a local variable. For example, the following expression can be used to compute the number of seconds per year.
seconds = Def "days_per_year"
(Num 365) (
Def "hours_per_day"
(Num 24) (
Def "minutes_per_hour"
(Num 60) (
Def "seconds_per_minute" (Num 60) (
Var "days_per_year"
‘Mul‘
Var "hours_per_day"
‘Mul‘
Var "minutes_per_hour"
‘Mul‘
Var "seconds_per_minute"
Similarly, the parameter of foldExpr now has an extra component: the ternary function def which corresponds to the ternary constructor Def. Notice that the last two arguments are recursive ones. We can now explain why the first use of environments is inferior to the second one. Trying to extend the first definition gives something like:
resultExprBad
:: Env Name Value -> Expr -> Value
resultExprBad env =
foldExpr ((+),(-),(),(),id,(env ?),error "def") The last component causes a problem: a body that contains a local definition has to
be evaluated in an updated environment. We cannot update the environment in this setting: we can read the environment but afterwards it is not accessible any more in the algebra (which consists of values). Extending the second definition causes no problems: the environment is now accessible in the algebra (which consists of computations). We can easily add a new action which updates the environment. The computation corresponding to the body of an expression with a local definition can now be evaluated in the updated environment.
f <+> g
= \env
-> f env + g env
f <-> g
= \env
-> f env - g env
f <> g
= \env
-> f env g env
f <> g
= \env
-> f env g env
6.4 Expressions
x <:=> f = \g env -> g ((x,f env):env) resultExprGood :: Expr -> (Env Name Value -> Value)
resultExprGood =
foldExpr ((<+>),(<->),(<>),(<>),const,flip (?),(<:=>)) ?resultExprGood seconds []
31536000 Note that by consing a pair (x,y) onto an environment (in the definition of the
operator <:=>), we add the pair to the environment. By definition of (?), the binding for x hides possible other bindings for x.