Adding variables
6.4.2 Adding variables
Our next goal is to extend the evaluator of the previous subsection such that it can handle variables as well. The values of variables are typically looked up in an environment which binds the names of the variables to values. We implement an environment as a list of name-value pairs. For our purposes names are strings and values are floats. In the following programs we will use the following functions and types:
type Env name value = [(name,value)] (?) :: Eq name => Env name value -> name -> value
env ? x = head [ v | (y,v) <- env, x == y] type Name
= String
type Value = Float The datatype and the corresponding algebra type and eval function are now as
follows. Note that we use the same name (Expr) for the datatype, although it differs from the previous Expr datatype.
Compositionality
data Expr = Expr ‘Add‘ Expr
| Expr ‘Min‘ Expr | Expr ‘Mul‘ Expr | Expr ‘Dvd‘ Expr | Num Value | Var Name
type ExprAlgebra a = (a->a->a -- add
,a->a->a -- min ,a->a->a -- mul ,a->a->a -- dvd ,Value->a -- num ,Name->a) -- var
foldExpr :: ExprAlgebra a -> Expr -> a foldExpr (add,min,mul,dvd,num,var) = 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
Expr now has an extra constructor: the unary constructor Var. Similarly, the ar- gument of foldExpr now has an extra component: the unary function var which corresponds to the unary constructor Var. Computing the result of an expression somehow needs to use an environment. Here is a first, bad way of doing this: one can use it as an argument of a function that computes an algebra (we will explain why this is a bad choice in the next subsection; the basic idea is that we use the environment as a global variable here).
resultExprBad
:: Env Name Value -> Expr -> Value
resultExprBad env = foldExpr ((+),(-),(),(),id,(env ?)) ?resultExprBad [("x",3)] (Var "x" ‘Mul‘ Num 2)
6 The good way of using an environment is the following: instead of working with a
computation which, given an environment, yields an algebra of values it is better to turn the computation itself into an algebra. Thus we turn the environment in a ‘local’ variable.
(<+>),(<->),(<>),(<>) :: (Env Name Value -> Value) ->
(Env Name Value -> Value) -> (Env Name Value -> Value)
f <+> g = \env -> f env + g env
f <-> g = \env -> f env - g env
f <> g = \env -> f env g env
6.4 Expressions
resultExprGood :: Expr -> (Env Name Value -> Value) resultExprGood =
foldExpr ((<+>),(<->),(<>),(<>),const,flip (?)) ?resultExprGood (Var "x" ‘Mul‘ Num 2) [("x",3)]
6 The actions ((+), (-), . . .) on values are now replaced by corresponding actions
((<+>), <->), . . . on computations. Computing the result of the sum of two subex- pressions within a given environment consists of computing the result of the subex- pressions within this environment and adding both results to yield a final result. Computing the result of a constant does not need the environment at all. Comput- ing the result of a variable consists of looking it up in the environment. Thus, the algebraic semantics of an expression is a computation which yields a value. This important statement can be summarised as follows.
algebraicSemantics :: InitialAlgebra -> Compute Value
In this case the computation is of the form env -> val. The value type is an example of a synthesised attribute. The value of an expression is synthesised from values of its subexpressions. The environment type is an example of an inherited attribute. The environment which is used by the computation of a subexpression of an expression is inherited from the computation of the expression. Since we are working with abstract syntax we say that the synthesised and inherited attributes are attributes of the datatype Expr. If Expr is one of the mutually recursive datatypes which are generated from the nonterminals of a grammar, then we say that the synthesised and inherited attributes are attributes of the nonterminal.