Difference between revisions of "Current implementation of multi constraint formals"
m (→Creation without call to explicit creation feature) |
|||
Line 64: | Line 64: | ||
</e> | </e> | ||
− | + | A creation like <e>create g</e> where ''g'' is of type ''G'' yields an error as | |
there are two versions of ''default_create'' available. | there are two versions of ''default_create'' available. | ||
Latest revision as of 09:39, 24 April 2007
Contents
Introduction
The first implementation of multi constraints is done and this article describes its current implementation status.
Let's take this example
MULTI [G -> {H, COMPARABLE rename is_equal as is_equal_cmp end}, H -> {NUMERIC rename default_create as default_create_num end, G}]
A type satisfying the constraint of G has the following properties:
- It conforms to whatever H is and therefore
- to
NUMERIC
and - by recursion to itself (G)
- to
- It conforms to its second constraint which is
COMPARABLE
.
A type satisfying the constraint of H has the following properties:
- It conforms to
NUMERIC
. - It conforms to G and therefore
- by recursion to itself (H) and
- to
COMPARABLE
.
As G and H both have to conform to each other the only possibility to satisfy this is that G and H must be the same in the generic derivation. It does not make much sense to program something like that but it is part of the current implementation.
Feature calls
Let's have a look at the following feature written in class MULTI:
feature g: G f: BOOLEAN -- Sample feature do create g -- 1 Result := g.is_equal(g) -- 2 end
The following rules have to hold for each call on a multi constraint formal:
- Every feature which is called has exactly one class which is used as the static type for the call.
This is a major difference to the ECMA standard which allows a call to features which are available through different classes as long as it is the same feature. A definition of what the same feature means can be found here.
In order to achieve that goal if a feature occurs with the same name in different classes one uses a rename clause and renames that feature. If there are name clashes between features and one does not want to call that feature a renaming is not needed.
As we perform calls to default_create
(line 1 of f
) and is_equal
(line 2 of f
, which are both features from ANY
and each occurs as the same feature once in COMPARABLE
and once in NUMERIC
. We do this despite the fact that it is actually the same feature. The reason for this can be found in the new semantics of the dynamic binding described in the standard. You may also want to have a look at the following article which discusses the issue.
Renaming on formals
The current implementation rejects a rename clause which is applied to a formal. The following code is therefore currently rejected as H is a formal and we don't allow a renaming on it.
class MULTI[G -> {H rename is_equal as is_equal_prime end, ANY}, H -> ANY]
The rules implemented are chosen so that they are the most restrictive. This is to ensure that written code is likely to compile in the future where the rules might be more relaxed.
Creation without call to explicit creation feature
Note: This is not implemented in interim release 6.0.6.7358 or older.
class MULTI [G -> {COMPARABLE rename default_craete as default_create2 end, NUMERIC} create default_create, default_create2 end]
A creation like create g
where g is of type G yields an error as
there are two versions of default_create available.
Implementation details
The current implementation is pretty straightforward and tries to be as non-intrusive as possible. Most things rely on computation rather than on memory. For example there's no list which contains all features properly renamed but only a relatively small instance of RENAMING_A
which contains a mapping of the name-ids.
The central classes to the current implementation are:
-
TYPE_SET_A
-
RENAMED_TYPE_A
-
RENAMING_A
-
CONSTRAINING_TYPE_AS
- and some changes to
CLASS_C
The main tasks which have to be performed are:
- Type checking in particular type conformance.
- Feature lookup
Both tasks are provided by TYPE_SET_A
.
TYPE_SET_A
Conformance
The feature conform_to
inherited from TYPE_A
can be used to check whether a type set conforms to another type which is possibly a type set as well.
- A type set conforms to another type if at least one type in the type
set conforms to the type.
- Another type conforms to a type set if it conforms to all types in the type set.
- A type set ts1 conforms to a type set ts2 if for each type t in ts2 there is at least one type in ts1 which conforms to t.
If every type is regarded as a type set with one element one can simply always use the last rule.
Feature lookup
There are various features which provide this functionality. Their differences are mostly in the amount of information they provide. Of course the computation gets more expensive if the feature returns more information. Currently the API is pretty much targeted to what the compiler needs. There's a bunch of features of the family e_feature_state_*/feature_i_state_* which are used when most likely only one feature occurs. As soon as you need more information the features of the family info_about_feature_* can help you to get all information about a certain feature for a given type set.
Now there is one more thing to add: As a type set which you obtain over {CLASS_C}.constraints (a_formal_position)
can contain formals, which themselves can have constraints and therefore add more available features, one needs to compute somehow a "flat" version of the type set. This version cannot be used anymore for meaning full type checking but is perfectly suited to do feature queries.
class MULTI [G -> {H, I}, H -> COMPARABLE, I -> NUMERIC]
Consider now the following code which works on the CLASS_C
object of class MULTI
called multi_class
a_type_set_with_formals := multi_class.constraints (1) -- a_type_set_with_formals: {H, I} a_type_set_without_formals := a_type_set_with_formals.constraining_types (multi_class) -- a_type_set_without_formals: {COMPARABLE, NUMERIC}
It basically gets rid of all formals (even recursive cases are handled correctly) and puts together a new type set which can be used for example to show all classes which provide features to a formal or to do feature lookup. The fact that there's no good reason to use it for conformance suggests that one refactors this class into two classes: One for type checking and one for feature lookup.
Associated class
This feature is no longer available and therefore exported to NONE. This is simply because there is no single associated class but many.
You can use the feature associated_classes instead and get a list of all associated classes.
However, the features applicable to the type set is not the same set as the features provided by each element of the associated_classes. This is because one loses the renaming (class RENAMING_A
) which is stored inside the TYPE_SET_A
in each RENAMED_TYPE_A
instance. This is why there are some places in the code where going from a CLASS_C
instance to one of type LIST [CLASS_C]
is not enough.
RENAMED_TYPE_A
The RENAMED_TYPE_A
class basically encapsulates a TYPE_A
object and a RENAMING_A
object.
The fact that it is conform to TYPE_A opens the door for mistakes: Make sure that you use {RENAMED_TYPE_A}.type
or {RENAMED_TYPE_A}.actual_type
if your code makes assignment attempts, it is pretty much the same as with the family of the "like" types.
RENAMING_A
This is the class which provides features to handle the renaming which is applied to the features of the constraint type.
It's current implementation is based on a hash table and therefore only one way is efficient.
You can find out whether a specific feature is renamed by calling is_feature_renamed
. There's also a predefined feature renamed
which takes an integer argument and returns another one. The integer returned is either
- unchanged, if the feature is not renamed
- some other positive integer if the feature is renamed under the renaming
- -1 if the feature was renamed into something else (therefore it is not accessible)
However, this is only relative to the renaming and is not in the context of a specific class. It means that if the feature f does not occur in the class you don't get an error (-1), as RENAMING_A
does not consult a context class for its operations.
CLASS_C
A major change here affects the feature constraint (i: INTEGER): TYPE_A
.
It is used to get the constraint of the formal at position i. A new precondition has been added which states that it is only ok to call that feature if the formal has only one constraint. This is also the reason why currently it is not allowed to apply a renaming to a single constraint.
For the multi constraint case the feature constraints (i: INTEGER): TYPE_SET_A
should be used instead. It returns a TYPE_SET_A instance which can possibly contain other formals.
List of changes done
This is an incomplete list of changes I've done which might need to be undone in the future.
- VGCC1 has a slightly adapted error message (which was wrong before) but is now also used for the case where one has two versions default_create in the creation constraint list.