11.3.1 Primitives

Evaluation and application are defined recursively. To perform an application, we need to evaluate all the subexpressions of the application expression, and then apply the result of evaluating the first subexpression to the values of the other subexpressions.

def is_application(expr): # requires: all special forms checked first
    return isinstance(expr, list)
   
def eval_application(expr, env):
    subexprs = expr
    subexprvals = map (lambda sexpr: meval(sexpr, env), subexprs)
    return mapply(subexprvals[0], subexprvals[1:])

The eval_application procedure uses the built-in map procedure, which is similar to list-map from Chapter 5. The first parameter to map is a procedure constructed using a lambda expression (similar in meaning, but not in syntax, to Scheme's lambda expression); the second parameter is the list of subexpressions.

The mapply procedure implements the application rules. If the procedure is a primitive, it "just does it": it applies the primitive procedure to its operands.

To apply a constructed procedure (represented by a Procedure), follow the stateful application rule for applying constructed procedures:
Charme Application Rule 2: Constructed Procedures. To apply a constructed procedure:

  1. Construct a new environment, whose parent is the environment of the applied procedure.
  2. For each procedure parameter, create a place in the frame of the new environment with the name of the parameter. Evaluate each operand expression in the environment or the application and initialize the value in each place to the value of the corresponding operand expression.
  3. Evaluate the body of the procedure in the newly created environment. The resulting value is the value of the application.

The mapply procedure implements the application rules for primitive and constructed procedures:

def mapply(proc, operands):
    if (is_primitive_procedure(proc)): return proc(operands)
    elif isinstance(proc, Procedure):
        params = proc.getParams()
        newenv = Environment(proc.getEnvironment())
        if len(params) != len(operands):
            eval_error ('Parameter length mismatch: %s given operands %s'
                               % (str(proc), str(operands)))
        for i in range(0, len(params)):
            newenv.add_variable(params[i], operands[i])        
        return meval(proc.getBody(), newenv)        
    else: eval_error('Application of non-procedure: %s' % (proc))