MathObjectsImpl.mesa
Copyright © 1989 by Xerox Corporation. All rights reserved.
Arnon, August 28, 1989 1:42:36 pm PDT
DIRECTORY
SafeStorage,
IO,
Atom,
Rope,
Basics,
Imager,
MathExpr,
MathObjects;
MathObjectsImpl: CEDAR PROGRAM
IMPORTS Rope
EXPORTS MathObjects
= BEGIN
Types From Other Interfaces
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Private Type Reps
ObjectRep: TYPE = RECORD [
class: Object ← NIL,
data: REFNIL
];
A possible alternative:
ObjectRep: TYPE = RECORD [
class: Object ← NIL,
data: REFNIL,
props: Atom.PropList
];
Such a props field would allow us to hang e.g. attributes off of specific instances of Objects, and not have to e.g. hardwire a slot for them into a RECORD structure pointed to by the .data field. This might enable more faithful emulation of e.g. the ODA or Tioga attribute machinery, and more generally, would provide a means of attaching "special case" (i.e. for this instance of the Object only) display control information to an Object.
A different alternative would be to simply implement the .data field of the Elements of a Domain or View as an Atom.PropList, so that all instance-specific information resides there. If Structures then kept their methods on their PropList, they would conform to this regime also, and we wouldn't need a special StructureDataRep.
This a better way to go, and in fact we can switch any Structure over to this way of doing business by simply revising its Impl appropriately.
Object: TYPE ~ REF ObjectRep; -- inside impl module, use rep
MethodRep: TYPE = RECORD [
staticType: ATOM,
name of method's "static type", which is a specification of the numbers (arities) of its inputs and outputs, and "some" information about their types. I.e. static types denote families of similar methods. Used for "dispatch on name of static type" invocation of methods (following check (and possible parameter recasting) that actual parameters conform to this method's "runtime type", which is computed afresh at each invocation of this method) via a global Alist of method types and corresponding method apply procs. The actual ATOM here is a selector into that global AList.
Static types themselves are generally defined (via type declarations) in some Cedar interface. Hence if some of a method's args are Cedar types (rather than MathStructures), this can be expressed in its static type.
Should the system be able to "verbalize" static types, so e.g. an external client can get them?
runtimeType: REF UnaryToTwoListsOp ← NIL,
PROC [Structure] RETURNS [LIST[args: LIST OF arg Structures, results: LIST OF result Structures] ]
Runtime type checking of Object args to this method; none done if = NIL.
Point of passing in a Structure (usually the Structure to which this method belongs) is that runtime types are typically (uniquely) parametrized by it.
Construed as "desired" arg Structures: assume, for example, that the args of this method are expected to be Elements (and not Structures). Here is the processing we will do to RecastArgs. Consider arg A with Structure S (S is either a Domain or View). If desired Structure Q is a Domain, and Q # S, we try to convert A into Q by first trying Q's Widen and Narrow procs (for direct Sub or Super Domain relations), then try to find a path from S to Q in the global Domain lattice. We may fail and exit. If Q is a Category, let C be S's Category. First check Q = C, or Q is a superCategory of C via "basic" inheritance, or via the global Category lattice. If success, create a (new Structure which is a) View V of S as Q, and create a new Object whose class is V and whose data is A.data. Suppose Q is a View, with underlying Domain D. Suppose S is a Domain. Then, as above, see if S can be converted to D; if so, then make the appropriate new Object in Q corresponding to A. If S is a View, then let D' be its underlying Domain, see if D' can be converted to D as above; if so, make the appropriate new Object in Q corresponding to A.
Note that there are two ways to "widen" or "narrow" a View: one is to change its Domain, the other to change its Category.
If one or more args are expected to be Domains or Views, then their desired Structures must be Categories.
Perhaps want some way to selectively turn off the check for individual args. Note that can pass Domains like "GeneralExpressions" or "DomainAndViewElements" to be very permissive about args.
Perhaps want some way to specify Cedar types in Runtime Types, e.g. REF, ROPE, or INT. Perhaps can have something like CedarType[INT], or CedarType[REF], in Runtime Types. Then can either do no checking of Cedar-typed args, or actually try to do some kind of checking for them (perhaps whatever checking Cedar Interpreter can do).
If output LIST OF arg Structures has i < n = (# args this method) Structures, then args i, i+1, ..., n expected to belong to last (ith) Structure of the list (useful e.g. for vector and matrix constructors).
Should the system be able to "verbalize" runtime types, so e.g. an external client can get them?
Notes for runtimeTypes of Methods in Categories:
1. Need a way to distinguish desired arg type of "Ring" from "Element of Ring".
2. Need a way to say that two "Element of Ring"s must come from the same Ring, or can come from different Rings.
proc: REF -- REF Proc
The "impl" of the method
props: Atom.PropList,
"Properties" and "attributes" of this method.
"Constant" attributes stored e.g. as [$commutative, $commutative] (NIL value field would confuse with absence of attribute)
props can be used to record alternative "keys" or "tags" or "names" for this method that may be of general interest (perhaps all methods should have unique "primary" keys). Such alternative keys might conceivably useful to an intelligent method lookup & selection routine (e.g. should I use SAC-2 or Maple factorization?) or View maker.
Use Atom.PropList, not RefTab, since # attributes expected small?
];
Method: TYPE ~ REF MethodRep; -- inside impl module, use rep
MethodDictionaryRep: TYPE = RefTab.Ref; -- ATOM keys; facilitates switch to Atom.PropList if desired
MethodDictionary: TYPE = REF MethodDictionaryRep; -- inside impl module, use rep
Static Types and Method Application
ApplyMethod: PUBLIC PROC [method: REF, structure: Object, argList: LIST OF REF, recast: BOOL] RETURNS[value: REF] =
m: Method;
ok: BOOLTRUE;
IF NARROW[method, Method] THEN m ← method ELSE m ← LookupMethodInStructure[method, structure]; -- FALSE => method arg is actually an ATOM method key; use it to lookup the method
Need an ERROR exit here if attempted method lookup fails
IF recast THEN [ok, outArgs] ← RecastArgs[m, structure, argList] ELSE outArgs ← argList; -- note that RecastArgs must handle argList as a list of REF's
ERROR exit here if attempted recast fails
IF ok THEN
Using m.type, search global StaticTypes AList of method types and applicator functions; when found, pass m and outArgs to applicator; it will make appropriately extract the successive elements of outArgs, handling Cedar types and Objects appropriately in so doing, pass the finalized input args to the dereferent of the appropriate narrowing of m.value, and return the result (narrowing it to an Object if possible, or caller should do that?)
ELSE ERROR; -- shouldn't be calling ApplyMethod unless you know it will succeed
END;
Impls of applicator procs for each of the following should be provided in the right places (e.g. FromINTOp in Ints), and then the [key, proc] pairs loaded into StaticTypes
MethodType: TYPE = {Value, SideEffect, TrueNullaryOp, NullaryOp, UnaryOp, BinaryOp, BinaryMixedOp, TernaryOp, TernaryMixedOp, QuaternaryOp, LegalFirstCharOp, ReadOp, FromRopeOp, ToRopeOp, ToExprOp, FromExprOp, FromBOOLOp, FromINTOp, UnaryPredicate, BinaryPredicate, CompareToZeroOp, BinaryCompareOp, StructuredToGroundOp, ElementRankOp, UnaryImbedOp, BinaryImbedOp, ListImbedOp, MatrixImbedOp, StructureFromSetConstructor, VectorStructureConstructor, SequenceStructureConstructor, MatrixStructureConstructor, PolynomialStructureConstructor};
END.