Online Manual: Connector-related Expressions
Inside an atomic classage, a few expressions can be used to operate on the connectors defined in the same classage:
- the connect e with P » Q expression (or called the connect expression). At runtime, it establish a connection with an objectage represented by e, and the host's connector P is matched against the connector Q of objectage e. Such an expression returns a value of the connection type, called a connection. If A is a singleton connector and is currently associated with one connection already, evaluating this expression will result in a Classages exception.
- the disconnect e statement (or called the disconnect statement). It disassociates the connection e from its lodging connector. After evaluating this expression, the connection representing e becomes stale.
- the e->m(e1,...,en) expression (or the connection invocation expression). At runtime, it invokes a method m (either an import or an export) belonging to the connection e. If at the moment e already is a stale connection, a Classages exception will be thrown.
- the A::m(e1,...,en) expression (or the singleton connector invocation expression). It is only used when A is a singleton connector. At runtime, it invokes a method m (either an import or an export) belonging to the current connection associated with A. If m is an import method and at this particular runtime moment there is no connection associated with A, a runtime Classages exception will be thrown.
- the m(e1, ..., en) expression (or the passive connection invocation expression). This expression can only be used within the scope of a connector (defined inside the export of a connector). At runtime, it invokes a method m (either an import or an export) belonging to the current connection associated with the enclosing connector. If m is an import method and at this particular runtime moment there is no connection associated with A, a runtime Classages exception will be thrown.
- the forall(p::A) {e} statement. This is the statement to enumerate all connections currently associated with connector A. Inside the scope of e, p can be used as a variable to denote each connection.
- the per-connection state access expressions. Reading from a per-connection state is achieved by the f expression, where f is per-connection state name. Writing to it is achieved by the f = e statement. These two expressions share the same syntactical form as local variable reading and assignment.
We disallow overriding in singleton connectors. Were it to be allowed, an export m serving as the default overridable method might have access to per-connection states and at the same time get invoked by the A::m(e1,...,en) expression. If at the moment the enclosing connector A does not have any connection associated, per-connection state access might lead to problems. Such an issue is avoidable by minor changes in the language design, and a more real reason is we do not see a need for such an operation.
We have written four examples to demonstrate the use of these expressions. The first example revolves around a generative connector, 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 connector in concern is a singleton, which means singleton connector invocation expression is allowed in this scenario. 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 connector while the fourth one revolves around a singleton.
Typechecking
When expression connect e with P » Q typechecks, it has a type of a connection type P. It fails to typecheck iff
- e does not have an objectage type.
- P does not exist as a connector defined in A.
- Q does not exist as a connector of the objectage represented by e.
- P as a connector allows values of the plug type or connection type to flow through. This can be decided by looking into the signatures of imports and exports defined in the connector.
- For P and Q, some import defined in one of
them is 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 disconnect e does not return a value. It fails to typecheck iff e is not of connection 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 connection type.
- method m is neither an import nor an export of e's connection type.
- expression e1, e2, ..., en does not conform to the declared type of m (up to subtyping).
When expression m(e1,...,en) within a connector 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 connector.
- expression e1, e2, ..., en does not conform to the declared type of m (up to subtyping).
Per-connection states are typechecked in a completely predictable way, the same as local fields.
We have explained the typechecking for the A::m(e1,...,en) expression and forall(p::A) {e} statement when dealing with plugger-related expressions.
A comprehensive example including most of the illegal cases mentioned above is here.