<< Classes, Refinement and Composition | Contents | Bindings >>
Dependent Types (dep.gen)
(dep.gen.1) Caesar supports all Java types and dependent types.
Def: A
dependent type is a type of form
exp.cls
, where
exp
is an expression returning an object which is an instance of a cclass, and
cls
is a virtual class of the object. The object is also called
family of the type, and expression
exp
is called
family expression.
Def: A
plain type is a normal Java type: a primitive type, an interface, a class (Java as well as Caesar class) or an array on another plain type.
Def: The
static type of an expression in a certain context is the most specific class, which is known to be the supertype of all possible runtime types, which can take the expression in this context.
Def: The
static type of a dependent type
exp.cls
is the inner class
cls
in the static type of the family expresion
exp
.
(dep.gen.2) Virtual class can be used as a dependent type if it is qualified with an expression, representing the path to its family object.
(dep.gen.3) Virtual class can be used as a plain type, if it is qualified by the names of its enclosing classes.
Family Expression (dep.famexp)
Family expression is the type declarations are limited only by the cases listed below.
(dep.famexp.1) Family expression may start from
-
this
reference
- reference to some outer object of
this
,
- a local variable, declared as final,
- a method argument (final),
- a static field (final).
(dep.famexp.2) Beginnings of form
this
and some outer of
this
may be also determined implicitly. The outer of
this
will be further denoted by
this.outer(1)
, the outer of outer by
this.outer(2)
and so on.
Listing 1.
public cclass Graph {
public boolean areConnected(Node n1, Node n2) {
...
}
public cclass Node {
...
}
public cclass Edge {
Node n1, n2;
Node getStart() { return n1; }
...
}
}
Example: All types used in List. 1 are actually dependent types, but all family expressions are implicit. References to
Node
or
Edge
in the context of
Graph
are assumed as correspondingly
this.Node
and
this.Edge
. In the context of
Edge
,
Node
types are assumed to be properties of owner graph, i.e.
this.outer(1).Node
. With explicit annotations the same example will look as in List. 2.
Listing 2.
public cclass Graph {
public boolean areConnected(this.Node n1, this.Node n2) {
...
}
public cclass Node {
...
}
public cclass Edge {
this.outer.Node n1, n2;
this.outer.Node getStart() { return n1; }
...
}
}
(dep.famexp.3) Family expression may contain any number of accesses to final class fields.
(dep.famexp.4) In family expression
exp.field
,
field
must be visible in the static type of
exp
in the context of type declaration.
Example: In List.3 the family expression of variable
a5
is
this.map.roads
. Here
this
is impicitly determined,
map
and
road
are final fields.
Listing 3.
public cclass Map { ...
public final Graph roads = new Graph();
public roads.Edge findRoad(String name) { ... }
}
public cclass MapTest {
final Map map = buildMap();
public void test() {
map.roads.Edge a5 = map.findRoad(name);
}
}
Places of Usage (dep.use)
(dep.use.1) Dependent types can be used as type declarations of
- class fields
- local variables
- method parameter types
- method return values
Type Checking (dep.check)
(dep.check.1) A type of an expression can be a plain type or a dependent type.
(dep.check.2) A dependent type of an expression is determined by its static type and its family. So a dependent type of an expression can be characterized by a pair
<fam, cls>
, where
fam
is the statically known family of the object returned by expression and
cls
is static type of the expression.
(dep.check.3) A dependent type
<fam, cls>
is a subtype of a plain type
type
if
cls
is a subtype of of
type
.
(dep.check.4) A dependent type
<fam1, cls1>
is a subtype of another dependent type if
<fam2, cls2>
if
fam1
is equivalent to
fam2
and
cls1
is a subclass of
cls2
.
Example: In List 4. all variables belong to family
this.map.roads
, therefore they can accept values only from this family of corresponding static types (
Graph.Edge
or
Graph.Node
).
Initialization of variable
a6
will fail because its initialization expression returns object of different family (
this.map.rivers
).
Initialization of
startA6
will fail, because the variable has static type
Graph.Node
, but its initialization expression returns an object with static type
Graph.Edge
.
Listing 4.
public cclass Map {
public final Graph roads = new Graph();
public final Graph rivers = new Graph();
public rivers.Edge findRiver(String name) { ... }
public roads.Edge findRoad(String name) { ... }
}
public cclass MapTest {
public final Map map = buildMap();
public void test {
map.roads.Edge a4 = map.findRoad("A4"); /* OK */
map.roads.Node startA5 = map.findRoad("A5").getStart(); /* OK */
map.roads.Edge a6 = map.findRiver("Elbe"); /* error */
map.roads.Node startA6 = map.findRoad("A6"); /* error */
}
}
Equivalence of Family Identities (dep.fameq)
(dep.fameq.1)A cannonical family expression of a family expression
exp
- has the form
base.field1...fieldN
,
- always refers to the family of
exp
at runtime
- and is valid at the context of
exp
.
Here
base
is
this
,
this.outer(n)
, a local variable, a formal method argument or a static field.
(dep.fameq.2) The families of two expressions are equivalent, when they can be represented the same cannonical family expressions.
If one of the families cannot be reduced to a cannonical form it is
not equivalent to any other family.
The cannonical family expression of an expression is determined by following rules:
- (dep.fameq.3) family expression of a local variable
loc
, is the family expression of the type declaration of the variable.
- (dep.fameq.4) family expression of a field access
exp.field
is an expression, obtained by substituting this
with exp
in the family expresion of the declared type of field
.
- (dep.fameq.5) family expression of a method call
exp.meth(..)
is an expression,
- obtained by substituting
this
with exp
in the family expresion of the declared return type of meth
, if the return type has this
or this.outer(n)
as base
- obtained by substituting formal argument name with the corresponding argument expression in the family expression of the declared return type, if the return type has the argument as family base.
- the declared family expression of the return type, in all other cases.
- (dep.fameq.6) expression of form
exp.field.outer(n)...
is reduced to expression exp2.outer(n-1)...
. Where exp2
is an expression, obtained by substituting this
with exp
in the family expresion of the declared type of field
. Terms outer(0)
are simply removed.
- (dep.fameq.7) expression of form
exp.meth(..).outer(n)...
is reduced to expression exp2.outer(n-1)...
. Where exp2
is an expression, obtained by substituting this
with exp
in the family expresion of the declared return type of meth
.
- (dep.fameq.8) family expression of a static field, is the family expression of the type declaration of the field.
- (dep.fameq.9) family expression of a formal argument in the method call
exp.meth(..)
is an expression,
- obtained by substituting
this
with exp
in the family expresion of the declared argument type, if the argument type has this
or this.outer(n)
as base
- obtained by substituting formal argument name with the corresponding argument expression in the family expression of the declared argument type, if the argument type has another argument as family base.
- the declared family expression of the argument type, in all other cases.
Example: Let's consider initialization of variable
startA5
in the List.4. We have to check if the type of its initialization expression
this.map.findRoad("A5").getStart()
is a subtype of the type of
startA5
.
The cannonical family expression of the variable
startA5
is the family expression of its type declaration (
dep.fameq.3), i.e.
this.map.roads
. So the type of
startA5
is
<this.map.roads, Graph.Node>
.
To determine the family expression of
this.map.findRoad("A5").getStart()
, we first look at the declared type of
getStart()
in List.1. It is
this.outer(1).Node
. According
dep.fameq.5 we substitute
this
in the family expression with
this.map.findRoad("A5")
and obtain
this.map.findRoad("A5").outer(1)
.
Now we have to reduce
this.map.findRoad("A5").outer(1)
further according
dep.fameq.7 The declared return type of
findRoad()
is
this.roads.Edge
. We take the family expression of this type and substitute
this
with
this.map
. So, we get
this.map.roads
. Now we can use the new expression to reduce outer access:
this.map.roads.outer(0)
.
outer(0)
is simply removed and we get, that the cannonical family expression of
this.map.findRoad("A5").getStart()
is
this.map.roads
and its type is
<this.map.roads, Graph.Node>
.
We have determined that the dependent type of the initialization expression of
startA5
is the same as the type of
startA5
. So this assignment is type safe.
<< Classes, Refinement and Composition | Contents | Bindings >>