[1] Even so, there will remain important aspects of the evaluation process that are not elucidated by our evaluator. The most important of these are the detailed mechanisms by which functions call other functions and return values to their callers. We will address these issues in chapter 5, where we take a closer look at the evaluation process by implementing the evaluator as a simple register machine.
[2] If we grant ourselves the ability to apply primitives, then what remains for us to implement in the evaluator? The job of the evaluator is not to specify the primitives of the language, but rather to provide the connective tissue—the means of combination and the means of abstraction—that binds a collection of primitives to form a language. Specifically:
  • The evaluator enables us to deal with nested expressions. For example, although simply applying primitives would suffice for evaluating the statement 1 + 6;, it is not adequate for handling 1 + (2 * 3);. As far as the primitive function + is concerned, its arguments must be numbers, and it would choke if we passed it the expression 2 * 3 as an argument. One important role of the evaluator is to choreograph function composition so that 2 * 3 is reduced to 6 before being passed as an argument to +.
  • The evaluator allows us to use variables. For example, the primitive function for addition has no way to deal with expressions such as x + 1. We need an evaluator to keep track of variables and obtain their values before invoking the primitive functions.
  • The evaluator allows us to define compound functions. This involves keeping track of function definitions, knowing how to use these definitions in evaluating expressions, and providing a mechanism that enables functions to accept arguments.
  • The evaluator provides the other constructs of the language such as sequential composition and conditional expressions.
4.1 The Metacircular Evaluator