EvalOp: MathObjects.BinaryToPairOp;
PROC [x: Object, e: Environment] RETURNS [result: Object, newEnv: Environment ← NIL];
UnpackOp: MathObjects.BinaryOp;
PROC [x: Object, e: Environment ← NIL] RETURNS [Object];
Return Object is either a CompositeExpr or a CompositeDisplayObject.
-- e specifies e.g. how many levels of unpacking to do, and also whether the current Domain/View S of each unpacked subObject should be "cached" in its unpacked form, via the $foo$[S, ...] convention.
Every Domain or View should have an $eltEval method for its Elements: if there isn't anything interesting to do, return the Object unchanged.
Ground Structures evaluate to themselves. For Structure constructors, the full semantics of Eval applies.
An example of an "interesting" thing to do is to consider the variables of a polynomial as symbols (i.e. names), look for values associated (which we assume to be Objects) with those names in e, (recursively) eval the values in environment e, substitute the results for the variables (by some regime of substitution, presumably simultaneous), and now "eval" the "substituted polynomial". We do so by assuming that the polynomial Domain has some "invertible" UnPackOp that we can apply to unpack any Element into a CompositeExpr U in which each of the variables the Element contains is an AtomicObject, and we apply Exprs.$eltEval (with Environment e) to U.
Of course, just because the UnPackOp was "invertible" within the polynmomial Domain (certainly we expect it to be if it caches Domains on the way down, and maybe it is even if it isn't), we really don't know what will happen after we have substituted for the variables and Exprs.$eltEval is applying MathEnvironments.AttemptMethod to build back up.
Our basic viewpoint is that a call to an EvalOp enters a new local "scope": thus newEnv records new [name, value] associations that are created by (e.g. evaluation of assignment statements within) this Eval, and also the removals of such associations (e.g. by evaluations of "Kill[Name]" or "kill[All]" ops) that happen within it, and makes no changes to env. It is the task (privilege) of the caller to determine whether to use the newEnv to make changes to env.