Difference between revisions of "Current implementation of multi constraint formals"

m (Creation without call to explicit creation feature)
 
Line 64: Line 64:
 
</e>
 
</e>
  
An creation like <e>create g</e> where ''g'' is of type ''G'' yields an error as
+
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

Construction.png Not Ready for Review: This Page is Under Development!

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)
  • 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

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

Information.png 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.