MathExprs.mesa
Last Edited by Arnon: September 6, 1989 10:51:10 am PDT
Ground Domain corresponding to General Expressions
External rep is "Exprs"
DIRECTORY
Rope,
MathObjects,
MathStructures;
MathExprs: CEDAR DEFINITIONS
~ BEGIN
Types From Other Interfaces
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Object: TYPE = MathObjects.Object;
Element Representation
Expr: TYPE = Object;
MathExprData: TYPE ~ REF MathExprDataRep; -- external abstract type
MathExprDataRep: TYPE ~
RECORD [
SELECT type:* FROM
atom => [
method: ATOM, -- type of entity, e.g. $integer, $real, $symbol
value: ROPE -- atomic value, e.g. integer as rope. Any legal name (e.g. Greek letter, subscripted variable, etc.) should be an atomic Expr of type $symbol (or we can have types $greekSymbol, $decoratedSymbol, etc., or $name ...). Better to have just one type $symbol, and plan to parse it to determine possible subcomponents, and so the right display.
],
compound => [
method: ATOM, -- operator
subObjects: LIST OF Object ← NIL -- operands
]
ENDCASE
];
Given our assumption of one or more EltToExpr procs in all domains (see below), allowing arbitrary Objects as subExprs, rather than limiting to Exprs, is essentially just being flexible with regard to the amount of local type inference that has been done on subExprs of an Expr. E.g. if we required subExprs to be Exprs, a user could of course at any time select a subExpr and EltEval (type narrow) it, and indeed he may be imagining in his mind that this has in fact been done for a subExpr of interest. And, of course, application of a EltToExpr proc can always undo the casting of any subObject as an element of a (narrower) domain. Hence it is appropriate to have a data representation that supports easily going back and forth between the two viewpoints.
Since we can have Expr Objects which are just an "outermost method" applied to Objects belonging to narrower domains, plus the assertion of membership of the whole in the Exprs domain, we make think of Exprs objecthood as a kind of "packaging", or "brush stroke", of collections of (other) Objects, that is tantamount to a declaration that we want to be relaxed, or insensitive, about the type of the total package. And, with EltToExpr procs, we have always the possibility of propagating this relaxedness to subObjects. And of course, by invoking eltEval$Exprs, we can always abrogate our relaxedness and try to limit the Object to a narrower domain.
Domain Representation
None - no Domain-specific data
Domain Constructors
DomMake: PROC RETURNS [domain: Object];
class = category = {Sets}
build methodDictionary
RETURN[MathObjects.MakeObject[class, [methodDictionary, NIL] ]
Element Conversion and IO
EltDisplay: MathObjects.UnaryOp;
Create a Display Object for a given Element.
Straightforward for atomic Exprs
For compound Exprs, recursively applies EltDisplay procs of subObject domains, then uses method and # of args to consult a domain-specific (i.e. for this domain) dbase of special formatting procs to look for match, else uses default functional form display.
Note that one cannot expect the domain of a subObject of a displayed Expr to necessarily be evident from its displayed appearance. E.g. when we see what looks like an integer, we don't know strictly from the rendered BoxTree for it whether it is the display of an element of Exprs, or an element of Integers. Same e.g. for "matrix of integers".
To facilitate system support for "letting a user model his data any way he wants to", we expect elements of all domains to have (one or more) "EltToExpr" procs, which produce a "completely Exprish" element of Exprs, i.e. a "pure functional" Expr, and which we would like to be "consistent" or "invertible" in at least one, preferably both of the following senses: 1) weaker: if you are given the destination domain, then by recursion, subObject instantiation, and composition, knowing domains in which to lookup the given method at each step, you can rebuild the object. 2) stronger: without being given the destination domain, then by recursion, subObject instantiation, and composition, using system type inference & resolution capabilities, and method lookup capabilities (including of course knowledge of the subObject domains at each step), you can rebuild the object.
Of course, the remarks below about absence of unique "natural" or "obvious" EltToExprRope procs apply equally to EltToExpr procs.
Assuming willingness to call instantiation of atomic subObjects "EltEval" (connoting e.g. evaluation (substitution and recursive evaluation until no change) of variables), then we are saying that we want "EltToExpr" and "EltEval" to be inverses of each other, at least in the weaker, and possibly also in the stronger, sense. Note that we are saying that EltEval means - recursively evaluate subObjects; given a variable, replace it by its value in the current environment (and recursively EltEval that value until no further change occurs); the result of each subObject EltEval is an object in some domain, then lookup a proc implementing the given method, in the "current environment", i.e. using the system's method lookup capabilities, and apply that proc to get a final object in some domain.
Thus we are implicitly suggesting that any use of pattern matching or rules in EltEval is expected to be bundled up inside some proc.
Domains may or may not support EltEval procs. If their Objects can contain "symbols" or "variables" (e.g. polynomials), rather than just "constants", then they probably should (because e.g. we may want to "eval in the presence of some environment". One typical way a domain can get an EltEval proc is by applying eltEval$Exprs to the result of a EltToExpr of the Object, since eltEval$Exprs is a locus of type inference & resolution, and method lookup, expertise.
In general, the implementor of a domain D should think carefully about what the ways in which clients might wish to "eltEval" Objects in it. If the verdict is that all elements of the domain are constants, then probably the domain should have a noOp $eltEval method, so that eltEval$Exprs[parse$D["<objectRep>"] ] can quickly do the right thing.
In general, of course, the $eltEval method for a structured domain simply recursively invokes the domains' $eltEval methods of subObjects, and then computes some function of the results. E.g. for matrix domains, it just evaluates the elements, then constructs up a new matrix to hold them. Again, this could be done by eltEval$Exprs[eltToExpr$Matrices(whatever)[<fooMatrix>] ].
EltRead: MathObjects.ReadOp;
The Domain-dependent parser for general expressions. Assumes that subObjects have been displayed with a EltToExprRope command, i.e. not their domain-dependent Ropes. Thus the Expr that it builds is "purely functional", i.e. subObjects belong to domain Exprs at all levels.
Note that we cannot in general expect elements of domains to have a "natural" or "obvious" EltToExprRope form, especially the more complex the domain is. In general, there is a range between "storing (a pointer to) a finished object spec" in the Rope, i.e. specifying the domain, hence its parser, and just giving a (possibly terse and incomprehensible) Rope that that parser can use to rebuild the (mathematical) object as an element of that domain, and "storing abstract syntax" in the Rope, i.e. putting out something like pure functional form, with little or no domain indications, and leaving it to the system's (general expression evaluator's) type inference & resolution capabilities, and method lookup capabilities, to rebuild the object, if the need arises.
EltToRope: MathObjects.ToRopeOp;
Write out an Element in such a form that the Domain parser can read it back in.
EltFromRope: MathObjects.FromRopeOp;
LegalFirstChar: MathObjects.LegalFirstCharOp;
EltLBKey: MathObjects.ToRopeOp;
LoganBerry primaryKey attribute value, for searching for this object in a LoganBerry db. Such keys are generated when an Object is to be offlined.
EltWrite: MathObjects.WriteOp;
WidenOther: MathObjects.BinaryOp;
Previously called "Recast"
Arguments are a DomainElement and a Domain. firstArg is intended to be an element of a "narrower" Domain than the one being defined by this interface, and secondArg is intended to be a Domain of the kind being defined by this interface. We attempt to widen firstArg to be an element of secondArg.
If CanWidenOther[firstArg], then either we return firstArg unchanged (it is already an element of secondArg), or we create and return a new Object belonging to secondArg, i.e. we never modify the input Object.
CanWidenOther: MathObjects.BinaryPredicate;
Previously called "CanRecast"
Arguments are either a DomainElement and a Domain, or two Domains. firstArg is intended to be an element of, or be, a "narrower" Domain than the one being defined by this interface, and secondArg is intended to be a Domain of the kind being defined by this interface. Returns true if firstArg can be widened to be an element of secondArg.
NarrowOther: MathObjects.BinaryOp;
Arguments are a DomainElement and a Domain. firstArg is intended to be an element of a "wider" Domain than the one being defined by this interface, and secondArg is intended to be a Domain of the kind being defined by this interface. We attempt to narrow firstArg to be an element of secondArg.
If CanNarrowOther[firstArg], then either we return firstArg unchanged (it is already an element of secondArg), or we create and return a new Object belonging to secondArg, i.e. we never modify the input Object.
CanNarrowOther: MathObjects.BinaryPredicate;
Arguments are either a DomainElement and a Domain, or two Domains. firstArg is intended to be an element of, or be, a "wider" Domain than the one being defined by this interface, and secondArg is intended to be a Domain of the kind being defined by this interface. Returns true if firstArg can be narrowed to be an element of secondArg.
WidenThis: MathObjects.UnaryOp;
Argument is a DomainElement belonging to a Domain of the kind being defined by this interface. Either we return it unchanged, or we widen it to be an element of some "wider" Domain. In the latter case, we create a new Object, i.e. don't modify input. However it is expected that only General Expressions could ever be returned unchanged by a WidenThis proc, since it should be possible to widen anything else into a General Expression.
For example, an integer can be widened to be a rational number.
Usually a (nontrivial) WidenThis proc is written by knowing some tag with which the intended domain can be looked up (so we don't need to mention the Cedar interface that defines it), and then just applying that Domain's WidenOther proc to the argument.
The basic AlgebraDomains method lookup procedure is to look for a method in this Object's class and all its superclasses, then set x ← WidenThis[input]; IF x.class.Equal[x, input] then FAIL else recursive call on x.
NarrowThis: MathObjects.UnaryOp;
Argument is a DomainElement belonging to a Domain of the kind being defined by this interface. Either we return it unchanged, or we narrow it to be an element of the narrowest possible "narrower" Domain. In the latter case, we create a new Object, i.e. don't modify input.
For example, the NarrowThis proc for the rational numbers should return a BigInt for 3/1, but (the unchanged input argument) BigRat for 3/5.
Expression evaluators are examples of NarrowThis procs. The fact that typically NarrowThis[V: Variable] looks for a value associated with V in some "environment", and if found, returns value.class.NarrowThis[value], is a detail that is entirely consistent with this assertion.
Domain Selectors
GetNameDomainData: MathObjects.GetDomainDataProc;
MathObjects.GetDomainDataProc: TYPE = PROC [domain: Object] RETURNS [DomainData: REF ANY];
output is a NameDomainData
Data1: MathObjects.UnaryOp;
Input is a domain, output is the data1 field of its DomainData (assuming this field is an Object)
Data2: MathObjects.UnaryOp;
Input is a domain, output is the data2 field of its DomainData (assuming this field is an Object)
Domain Operations
Flavor: MathObjects.FlavorOp;
MathObjects.FlavorOp: TYPE = PROC [object: Object] RETURNS [flavor: Flavor];
MathObjects.flavor: TYPE = { DomainElement, Domain, Class};
Looks (with ISTYPE) for ElementNameData and NameDomainData.
Should be in both DomainElement and Domain class records.
DomToRope: MathObjects.ToRopeOp;
A functional expression that can be parsed and evaluated to reconstruct the domain
DomName: MathObjects.ToRopeOp;
A short identifying name, e.g. "Q" for the rational numbers
DomLBKey: MathObjects.ToRopeOp;
LoganBerry primaryKey attribute value, for searching for this object in a LoganBerry db. Such keys are generated when an Object is to be offlined.
DomData: MathObjects.UnaryToListOp;
selector: returns LIST[data1, data2] of a Name Domain
IsNameDomain: MathObjects.UnaryPredicate;
Element Constructors
MakeName: MathObjects.ListImbedOp;
Arguments are the fields of an ElementNameData.
MakeElementNameData: PROC [field1, field2] RETURNS [elementData: ElementNameData];
Element Selectors
GetNameElementData: MathObjects.GetElementDataProc;
MathObjects.GetElementDataProc: TYPE = PROC [domainElement: Object] RETURNS [ElementData: REF ANY];
output is a ElementNameData
Field1: MathObjects.UnaryOp;
Input is a domain, output is the field1 field of its ElementData (assuming this field is an Object)
Field2: MathObjects.UnaryOp;
Input is a domain, output is the field2 field of its ElementData (assuming this field is an Object)
Element Operations
BinaryOp: MathObjects.BinaryOp;
UnaryOp: MathObjects.UnaryOp;
END.