• No results found

val m : int = 3 val n : int = m*m in m*n end

This expression has type int and value 27, as you can readily verify by first calculating the bindings formandn, then computing the value ofm*n relative to these bindings. The bindings formandnare local to the expres- sionm*n, and are not accessible from outside the expression.

If the declaration part of aletexpression eclipses earlier bindings, the ambient bindings are restored upon completion of evaluation of the let expression. Thus the following expression evaluates to54:

val m : int = 2 val r : int = let val m : int = 3 val n : int = m*m in m*n end * m

The binding of m is temporarily overridden during the evaluation of the letexpression, then restored upon completion of this evaluation.

3.5

Typing and Evaluation

To complete this chapter, let’s consider in more detail the context-sensitivity of type checking and evaluation in the presence of bindings. The key ideas are:

• Type checking must take account of the declared type of a variable.

• Evaluation must take account of the declared value of a variable. This is achieved by maintaining environments for type checking and evaluation. The type environmentrecords the types of variables; the value

3.5 Typing and Evaluation 30 environment records their values. For example, after processing the com- pound declaration

val m : int = 0

val x : real = Math.sqrt(2.0) val c : char = #"a"

the type environment contains the information val m : int

val x : real val c : char

and the value environment contains the information val m = 0

val x = 1.414 val c = #"a"

In a sense the value declarations have been divided in “half”, separating the type from the value information.

Thus we see that value bindings have significance for both type check- ing and evaluation. In contrast type bindings have significance only for type checking, and hence contribute only to the type environment. A type binding such as

type float = real

is recorded in its entirety in the type environment, and no change is made to the value environment. Subsequently, whenever we encounter the type constructorfloatin a type expression, it is replaced byrealin accordance with the type binding above.

Inchapter 2we said that a typing assertion has the formexp:typ, and that an evaluation assertion has the formexp⇓val. While two-place typing and evaluation assertions are sufficient for closedexpressions (those with- out variables), we must extend these relations to account for openexpres- sions (those with variables). Each must be equipped with an environment

recording information about type constructors and variables introduced by declarations.

Typing assertions are generalized to have the form

3.5 Typing and Evaluation 31 typenv ` exp : typ

where typenv is atype environment that records the bindings of type con- structors and the types of variables that may occur in exp.2 We may think oftypenvas a sequence of specifications of one of the following two forms:

1. type typvar = typ

2. val var : typ

Note that the second form does not include the binding for var, only its type!

Evaluation assertions are generalized to have the form

valenv ` exp ⇓ val

where valenv is a value environment that records the bindings of the vari- ables that may occur inexp. We may think ofvalenvas a sequence of spec- ifications of the form

val var = val

that bind the valuevalto the variablevar.

Finally, we also need a new assertion, calledtype equivalence, that de- termines when two types are equivalent, relative to a type environment. This is written

typenv ` typ1 ≡ typ2

Two types are equivalent iff they are the same when the type constructors defined intypenvare replaced by their bindings.

The primary use of a type environment is to record the types of the value variables that are available for use in a given expression. This is expressed by the following axiom:

. . .val var : typ. . .`var:typ

2Theturnstilesymbol, “`”, is simply a punctuation mark separating the type environ-

3.6 Sample Code 32

In words, if the specificationval var : typoccurs in the type environment, then we may conclude that the variablevarhas typetyp. This rule glosses over an important point. In order to account for shadowing we require that the rightmost specification govern the type of a variable. That way re-binding of variables with the same name but different types behaves as expected.

Similarly, the evaluation relation must take account of the value envi- ronment. Evaluation of variables is governed by the following axiom:

. . .val var = val. . .`var⇓val

Here again we assume that thevalspecification is the rightmost one gov- erning the variablevarto ensure that the scoping rules are respected.

The role of the type equivalence assertion is to ensure that type con- structors always stand for their bindings. This is expressed by the follow- ing axiom:

. . .type typvar = typ. . .` typvar≡typ

Once again, the rightmost specification fortypvargoverns the assertion.

3.6

Sample Code

Hereis the complete code for this chapter.

Chapter 4

Functions

4.1

Functions as Templates

So far we just have the means to calculate the values of expressions, and to bind these values to variables for future reference. In this chapter we will introduce the ability toabstractthe data from a calculation, leaving behind the bare pattern of the calculation. This pattern may then beinstantiated

as often as you like so that the calculation may be repeated with specified data values plugged in.

For example, consider the expression2*(3+4). The data might be taken to be the values 2, 3, and 4, leaving behind the pattern * ( + ), with “holes” where the data used to be. We might equally well take the data to just be 2 and 3, and leave behind the pattern * ( + 4). Or we might even regard * and + as the data, leaving 2 (3 4) as the pattern! What is important is that a complete expression can be recovered by filling in the holes with chosen data.

Since a pattern can contain many different holes that can be indepen- dently instantiated, it is necessary to givenamesto the holes so that instan- tiation consists of plugging in a given value for all occurrences of a name in an expression. These names are, of course, just variables, and instan- tiation is just the process of substituting a value for all occurrences of a variable in a given expression. A pattern may therefore be viewed as a

functionof the variables that occur within it; the pattern is instantiated by

applyingthe function to argument values.