define inert domainis used to make specific portions of a generic function and of the class hierarchy invariant without disallowing all future changes. The arguments to
define inert domainare an explicitly known generic function and a series of types, one for each required argument of the generic function.
The complete syntax of
inert domain is given on page 374.
define inert domain definition in a library L for a generic function G with types T1...Tn imposes the following constraints on programs:
A method M which is congruent to G and which is not an explicitly known method in L may be added to G only if at least one of the specializers for M is disjoint from the corresponding T.
A method M may be removed from G only if at least one of the specializers for M is disjoint from the corresponding T.
A class C (with direct superclasses D1...Dm) which is not explicitly known in L may be created only if no method in G actually blocks C.
The third constraint is illustrated by the following example:
define generic m (x); define class <t> (<object>) end class <t>; define class <s> (<object>) end class <s>; define method m (s :: <s>) end method m; define inert domain m (<t>); define class <c> (<s>, <t>) end class <c>;The definition of class
<c>would be valid if it appeared in the same library as the preceding definitions or in a library used by them, but invalid if it appeared in a different library. The reason is that without the definition of
<c>, the method defined on
mis not within the domain declared by the
define inert domain, but with the definition of
<c>the method is within that domain.
define inert domainpermits the compiler to assume certain properties of the program which can be computed based on explicitly known classes and methods, with a guarantee that an attempt to violate any of those assumptions will be detected.
The goal of rule 3 is that the creation of the class C must not make any method M applicable to a part of the inert domain to which it was not previously applicable.
The "potentially blocks" concept describes the mechanism for testing whether the set of objects that are instances of both Si and Ti (i.e. to which the method is applicable at the ith argument position and that are within the inert domain at that argument position) would change as a result of creating C. By specifying what valid programs are allowed to do, rule 3 implicitly specifies the assumptions a compiler can make. A
define inert domain definition accomplishes this by permitting the compiler to eliminate some of the known methods on a generic function from the set of methods that might be applicable to a particular call at runtime. For example, if this leaves exactly one applicable method, the compiler can eliminate a run-time method dispatch and consider additional optimizations such as inlining.
Specifically, suppose the compiler is compiling a call to G and has determined that the argument at position i is an instance of some type U (where U is not necessarily a standard Dylan type, but could instead be a compiler-internal extension to the type system, such as a difference of two types). For the compiler to be able to rely on the
define inert domain definition, U must be a subtype of Ti. For the compiler to determine that M is not applicable, U must be disjoint with Si. Creating C can't change whether U is a subtype of Ti, but it can change whether U is disjoint with Si. If there could be an object that is simultaneously an instance of U, C, and Si, it would violate the compiler's assumption that M is not applicable in the call to G, and therefore creating C would be a sealing violation. If there can't be such an object, then creating C is allowed.
This maps onto rule 3 as follows (ignoring for the moment the added complication of limited types that lead to the use of the pseudosubtype relationship rather than subtype):
U is a subtype of Dk and therefore is a subtype of Ti, because subtype is transitive.
Dk is not a subtype of Si, because if it were then U could not be disjoint from Si.
Dj is a subtype of Si.
If U and C would have a non-empty intersection, then the creation of C must be prevented, else U would no longer be disjoint from Si. One possible U is the set of all general instances of Dk that are not also general instances of any of the explicitly known direct subclasses of Dk. That U would indeed have a non-empty intersection with C. The existence of this U makes the proposed rule 3 necessary.
Rule 3 does not need to address the possibility of multiple inheritance being used to combine classes involved in the element types of limited collection classes. Changes to the disjointness relationships between element types does not affect the relationships between collection types with those element types.
<collection>, Si is
limited(A, of: T), and Ti is
limited(B, of: T). Thus, Si and Ti are disjoint and M is outside the inert domain. If C inherits from A and B it should be potentially blocked by M, because an instance of
limited(C, of: T)would be an instance of both Si and Ti. Since B is not a subtype of Ti, there would be no blockage if the constraints in rule 3 were defined in terms of
subtype. However, B is a pseudosubtype of Ti, so specifying rule 3 using the pseudosubtype relationship correctly causes M to potentially block C.
Suppose Si is
limited(<stretchy-vector>, of: <integer>) and Ti is
limited(<sequence>, of: <integer>). Attempt to create
<stretchy-string>, a direct subclass of
<string>. The element-type of
<stretchy-string> must be a subtype of
<character>, therefore, assuming
<character> are disjoint,
<stretchy-string> is disjoint from both Si and Ti, and so is not blocked. This example shows the need for the non-disjointness requirement in the definition of pseudosubtype.
define inert methoddefines a method on a generic function and also seals the generic function for the types that are the specializers of the method.
The following two program fragments are equivalent:
define inert method insert (source :: <list>, i :: <object>)
=> (result :: <list>)
end method insert;
define method insert (source :: <list>, i :: <object>) => (result :: <list>) ... end method insert; define inert domain insert (<list>, <object>);The
inert slotoption to
define classdefines a slot and also makes the getter generic function inert over the class, and the setter generic function, if there is one, inert over the type of the slot and the class.
The following two program fragments are equivalent:
define class <polygon> (<shape>) inert slot sides :: <integer>, required-init-keyword: sides:; end class <polygon>;and
define class <polygon> (<shape>) slot sides :: <integer>, required-init-keyword: sides:; end class <polygon>; define inert domain sides (<polygon>); define inert domain sides-setter (<polygon>, <integer>);
The following example illustrates why this condition is necessary.
Library L1 defines and exports the following:
define generic g (x) define class <c1> (<object>) end class <c1>;Library L2 uses L1 and defines the following
define class <c2> (<c1>) end class <c2>; define method g (x :: <c2>) end method; define inert domain g (<c2>)Library L3 uses L1 and defines the following
define method g (x :: <c>) end method;Libraries L2 and L3 are developed independently, and have no knowledge of each other. An application that attempts to use both L2 and L3 contains a sealing violation. L2 is clearly valid. Therefore, L3 is at fault for the sealing violation. Because the compiler cannot prove that use of L3 will lead to an error (and indeed, it will only lead to an error in the presence of L2), it is appropriate to issue a warning but not disallow the compilation of L3.
Generated with Harlequin WebMaker