Online Manual: Plugger-related Expressions
Inside an atomic classage, a few expressions can be used to operate on the pluggers defined in the same classage:
- the plugin A with P » Q expression (or called the plugin expression). At runtime, it creates the runtime instance of classage A within the objectage currently hosting this expression, and the host's plugger P is matched against the mixer Q of classage A. Such an expression returns a value of the plug type, called a plugging. If A is a singleton plugger and is currently associated with one plugging already, evaluating this expression will result in a Classages exception.
- the unplug e statement (or called the unplug statement). It disassociates the plugging e from its lodging plugger. After evaluating this expression, the plugging representing e becomes stale.
- the e..m(e1,...,en) expression (or the plugging invocation expression). At runtime, it invokes a method m (either an import or an export) belonging to the plugging e. If at the moment e already is a stale plugging, a Classages exception will be thrown.
- the A::m(e1,...,en) expression (or the singleton plugger invocation expression). It is only used when A is a singleton plugger. At runtime, it invokes a method m (either an import or an export) belonging to the current plugging associated with A. If m is an import method and at this particular runtime moment there is no plugging associated with A, a runtime Classages exception will be thrown.
- the m(e1, ..., en) expression (or the passive plugging invocation expression). This expression can only be used within the scope of a plugger (defined inside the export of a plugger). At runtime, it invokes a method m (either an import or an export) belonging to the current plugging associated with the enclosing plugger. If m is an import method and at this particular runtime moment there is no plugging associated with A, a runtime Classages exception will be thrown.
- the forall(p::A) {e} statement. This is the statement
to enumerate all pluggings currently associated with plugger
A. Inside the scope of e, p can be used
as a variable to denote each plugging.
We have written four examples to demonstrate the use of these expressions. The first example revolves around a generative plugger, including the use of all the above expressions, when things go smoothly, or when exceptions should be thrown. The second example is similar to the first one, but here the plugger in concern is a singleton, which means singleton plugger invocation expression is allowed in this scenario, and there are important cases such as to demonstrate the correctness of dynamic overriding. The third example and the fourth example are similar to the first and the second, except that we now consider the correctness of these expressions in a bigger context: when an atomic classage contains any of the aforementioned expressions and is later used in mixing to form bigger compounds, does Classages still work properly? Running these examples will give readers the positive answer. Again, the third example revolves around a generative plugger while the fourth one revolves around a singleton.
Typechecking
When expression plugin A with P » Q typechecks, it has a type of a plug type P. It fails to typecheck iff
- A does not exist as a classage.
- P does not exist as a plugger defined in A.
- Q does not exist as a mixer defined in A.
- P as a plugger allows values of the plug type to flow ``upward'' toward the root of the plugin hierarchy. This can be decided by looking into the signatures of imports and exports defined in the plugger.
- For P and Q, some import defined in one of
them does not have a default export, and at the same time not
satisfied by an exported defined in the other. (dangling imports).
- If P declares a method (say m) as an import
and Q declares m as an export, the signature
of m in Q is not the same or more specialized than
that in P (i.e. failure to conform to a subtyping
property). vice versa for the situation when Q
declares an import and P does an export.
Statement unplug e does not return a value. It fails to typecheck iff e is not of plug type.
When expression e..m(e1,...,en) typechecks, its type is the same as the return type of the method m. It fails to typecheck iff
- expression e does not have a plug type.
- method m is neither an import nor an export of e's plug type.
- expression e1, e2, ..., en does not conform to the declared type of m (up to subtyping).
When expression A::m(e1,...,en) typechecks, its type is the same as the return type of the method m. It fails to typecheck iff
- A is not a mixer, nor a singleton plugger, nor a singleton connector (mixer invocation and singleton connector invocation share the same grammar).
- method m is neither an import nor an export of A.
- expression e1, e2, ..., en does not conform to the declared type of m (up to subtyping).
When expression m(e1,...,en) within a plugger typechecks, its type is the same as the return type of the method m. It fails to typecheck iff
- method m is neither an import nor an export of the enclosing plugger.
- expression e1, e2, ..., en does not conform to the declared type of m (up to subtyping).
Statement forall(p::A) {e} does not return a value. It fails to typecheck iff
- A is not a plugger nor a connector (connection enumeration shares the same grammar).
- expression e typechecks with the assumption that p is of plug type A (or in the connector case, of connection type A).
For all the three invocation expressions (plugging invocation, singleton plugger invocation and passive invocation), an extra note is when method m is both an export and an import in the plugger (the overriding case with default implementation). If the import and the export have different signatures (the export one being more specialized according to the well-formedness of classages), the signature applied for typechecking the invocations should be the import one to ensure type soundness.
A comprehensive example including most of the illegal cases mentioned above is here.