Structure Types
By a "Structure:" we mean a Domain, View, or Category (as distinct from an "Element" of some Domain or View).
StructureRep:
TYPE =
RECORD [
methods: MethodDictionary, -- methods of Category, Domain, View uniformly accessible
data: REF ← NIL -- optional structure-specific data, e.g. a View should save its "underlying" Domain, Domains and Categories may have "properties", e.g. we may attach axioms to a Category.
];
Structure: TYPE ~ REF StructureRep; -- inside impl module, use rep
We hope to avoid explicitly recording whether a Structure is Domain, View, or Category. A IsDomain proc can be made by asking the Structure to ToRope itself and then parsing some prefix, checking in global data structures (e.g. Domains lattice, Category lattice, registered Domain constructors, registered Category constructors; Views can always be identified by presence of "AsA" in their print names, e.g. "Integers AsA AbelianGroup AsA Monoid").
When a Domain or View or Category is instantiated, we get a link Domain/View -> Category via the .class field. This is an instance of the "isAnElementOf" relation that in general we want an Object's .class field to express. In this case it may be of use for inheritance, in that Domains can "inherit" methods from a Category they belong to. However, we view it as merely a particular favored "basic" inheritance relation. In general, (multiple) inheritance is supported via the global Domain and Category lattices, that record all "isASubsetOf" relations among Domains and Categories, respectively. There is a certain consistency assumed between these lattices and the capabilities of the Domains and Categories involved in them. If we are trying to recast (an element of) Domain or Category S as (an element of) S', and if there is path from S to S' in the global lattice, then we assume that by (e.g. depth-first or breadth-first) search of the Widen or Narrow (depending on the direction of the path) edges rooted at S', we will find a "path" of procs which can be composed to actually do the recasting. Thus in general, the "transitive closure" of the Widen or Narrow "edges" rooted at a Structure S comprise a subgraph of one of the global lattices. The possible partiality of this subgraph is the major reason for the use of the global lattices: they give us a single place to express the full multiplicity of relationships among the Structures that are their nodes.
Note that a Domain's .class field creates one, and only one, link between the Domain and Category lattices. Perhaps we need the ability for more such links (-this is the point of Views?). Perhaps there should actually be only one lattice?
A Domain should always be able to make a View of itself as a member of its category. This should normally be straightforward: the Category methods should be a subset of the Domain methods, with identical names. We may even go so far as to say that the elements of Categories ARE Views; setting the Class field of a Domain to point to a Category is just a "lazy" binding; it is becomes meaningful when we create the View of the Domain as a member of that Category.
Given a category C, a View V that is a member of C, and a (direct) supercategory C' of C, C should always know how to create a new View V' which is a View of V as a member of C'. This will typically involve retaining a subset of V's methods, and possibly giving some of them new names. We call this capability the "ViewWiden" method of C'. If C is not a direct subcategory of C', then we simply compose the ViewWiden methods along a chain of intermediate subcategories.
Perhaps in general, Widen and Narrow procs should not reside in Structures; they are more properly thought of as labels on edges in the global lattices. Hence when a Domain is instantiated, it should
1) create a node for itself in the Domain lattice
2) identify (Narrow and Widen) edges in the Domain lattice of which its a vertex, and provide a proc for each such edge. There should be a precedence ordering among the Narrow edges, and among the Widen edges, to guide searches.
3) provide at least one link (edge) from itself as a node in the Domain lattice, to a node of the Category lattice, together with a ViewAs proc for each such edge. Its .class field is one such link (except does not provide the ViewAs proc per se). Again, there should be a precedence ordering among these edges.
When a Category is instantiated, it should
1) create a node for itself in the Category lattice
2) identify (Widen) edges in the Category lattice of which its a vertex, and provide a proc for each such edge. (Narrow procs seem impossible for Categories; e.g. for Ring -> ID, how do you give a proc that determines whether a given Ring has zero divisors?). There should be an ordering among these Widen edges.
Some global proces may, from time to time, crawl over all links from the Domain lattice into the Category lattice, and update the aggregate of all "back" links from nodes of the Category lattice to nodes of the Domain lattice. This can be regarded as system data, namely the ability to report all currently instantiated Domains that can be viewed as elements of a particular Category (transitive closure in the Category graph should of course be used to generate the full such list).
There should be allowance for "equipotent" nodes in (at least) the Domain lattice, typically arising from Domains which are multiple representations for the same mathematical domain. Each such pair can be both "widened" and "narrowed" to each other.