|
|
| (3 intermediate revisions by the same user not shown) |
| Line 1: |
Line 1: |
| − | [[Category: ECMA]]
| + | Replaced by [[restrict]] |
| − | [[Category:Catcall]]
| + | |
| − | {{Research}}
| + | |
| − | {{UnderConstruction}}
| + | |
| − | | + | |
| − | ==Introduction==
| + | |
| − | | + | |
| − | By allowing covariant feature redefinition and hiding of features, the Eiffel language introduces the problem of cat-calls (Changed Availability or Type). The restrict mechanism prevents this by introducing new derived types whenever a feature is covariantly redefined or the export status is restricted. These derived types are then used to prevent cat-calls through the conformance rules to the original types.
| + | |
| − | | + | |
| − | This mechanism is a variant on forget/keep as originally envisaged by Mark Howard. This variant was worked out by us when he explained to me his ideas about forget/keep.
| + | |
| − | | + | |
| − | ===Example classes===
| + | |
| − | | + | |
| − | The following classes will be used for illustration:
| + | |
| − | | + | |
| − | {| class="top-aligned"
| + | |
| − | |
| + | |
| − | <e>
| + | |
| − | class ANIMAL
| + | |
| − | | + | |
| − | feature
| + | |
| − | | + | |
| − | eat (f: FOOD)
| + | |
| − | | + | |
| − | sleep
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | |
| + | |
| − | <e>
| + | |
| − | class CAT
| + | |
| − | | + | |
| − | inherit
| + | |
| − | | + | |
| − | ANIMAL
| + | |
| − | redefine
| + | |
| − | eat
| + | |
| − | export
| + | |
| − | {NONE} sleep
| + | |
| − | end
| + | |
| − | | + | |
| − | feature
| + | |
| − | | + | |
| − | eat (f: CAT_FOOD)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | |
| + | |
| − | <e>
| + | |
| − | class FOOD
| + | |
| − | | + | |
| − | end
| + | |
| − | | + | |
| − | | + | |
| − | class CAT_FOOD
| + | |
| − | | + | |
| − | inherit
| + | |
| − | | + | |
| − | FOOD
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | |
| + | |
| − | <e>
| + | |
| − | class LIST [G]
| + | |
| − | | + | |
| − | feature
| + | |
| − | | + | |
| − | has (g: G): BOOLEAN
| + | |
| − | | + | |
| − | put (g: G)
| + | |
| − | | + | |
| − | item: G
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | |}
| + | |
| − | | + | |
| − | ===The problem===
| + | |
| − | | + | |
| − | The problem of cat-calls has two variants, one because a feature is covariantly redefined:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | food: FOOD
| + | |
| − | do
| + | |
| − | | + | |
| − | a := c
| + | |
| − | -- illegal since `eat' for type CAT takes arguments only of type CAT_FOOD
| + | |
| − | a.eat (food)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | And one because the visibility of features change:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | do
| + | |
| − | | + | |
| − | a := c
| + | |
| − | -- `sleep' is hidden for type CAT and should not be called
| + | |
| − | a.sleep
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ===The idea===
| + | |
| − | | + | |
| − | To solve the problem of cat-calls, the idea is that whenever you covariantly redefine a feature of a parent type, you actually inherit from a (virtual) parent type which does not have this feature at all, but you then re-introduce the feature to the new class (with restricted export status). The same goes for changing the export status.
| + | |
| − | | + | |
| − | As a consequence of this, the types as we know them don't necessarily conform to each other. If the type <e>CAT</e> inherits from the type <e>ANIMAL</e> but covariantly redefines the feature <e>eat</e> then the type <e>CAT</e> does not conform to <e>ANIMAL</e> anymore, but only to a type <e>ANIMAL</e> without the feature <e>eat</e>.
| + | |
| − | | + | |
| − | | + | |
| − | ==Restrict mechanism==
| + | |
| − | | + | |
| − | The restrict mechanism introduces a new keyword <e>restrict</e> which can be used to covariantly redefine features. By removing features from the class interface, a new type is introduced. The new type has all the features that the original type had, except those listed in the restrict clause - these latter features are present in the class, but exported to {NONE}. If <e>restrict all</e> is used, all features in the original will be removed from the new type. This produces a type with no features at all, which isn't very useful on its own. But it is useful if you want to lose all except a few of the features. We introduce another new keyword <e>except</e> which can only follow <e>restrict all</e> (therefore it does not need to be a completely reserved word).
| + | |
| − | | + | |
| − | ===Default behavior===
| + | |
| − | | + | |
| − | In the restrict mechanism the normal declaration of a type will have all the features of a type from which it inherits. This implies that all subtypes which reduce an export status or covariantly redefine a feature don't conform to this type anymore.
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | -- normal declaration means we have all the features
| + | |
| − | animal: ANIMAL
| + | |
| − | cat: CAT
| + | |
| − | do
| + | |
| − | -- legal since the feature `eat' is in the normal type
| + | |
| − | animal.eat (food)
| + | |
| − |
| + | |
| − | -- legal since the feature `sleep' is in the normal type
| + | |
| − | animal.sleep
| + | |
| − | | + | |
| − | -- illegal since CAT does not conform to ANIMAL anymore.
| + | |
| − | -- N.B. The declaration of CAT given above is illegal under this proposal.
| + | |
| − | -- Adding the restrict keyword will make it legal again, and emphasize the lack of conformance.
| + | |
| − | animal := cat
| + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ===Syntax===
| + | |
| − | | + | |
| − | <e>
| + | |
| − | -- normal declaration is an object with all features and no
| + | |
| − | -- subtype which has a covariantly redefined feature conforms to this type
| + | |
| − | a1: ANIMAL
| + | |
| − | | + | |
| − | -- a type which lacks any exported features, thus all
| + | |
| − | -- subtypes conform to this type. It is not a very useful type.
| + | |
| − | a2: ANIMAL restrict all end
| + | |
| − | | + | |
| − | -- a type which only restricts the features `eat' and `sleep'. All subtypes which only redefine or change export status
| + | |
| − | -- of `eat' or `sleep' will conform to this type
| + | |
| − | a3: ANIMAL restrict eat export {NONE} sleep end
| + | |
| − | | + | |
| − | -- a type which lacks any exported features other than eat.
| + | |
| − | a4: ANIMAL restrict all except eat end
| + | |
| − | | + | |
| − | </e>
| + | |
| − | | + | |
| − | | + | |
| − | ==Covariant feature redefinition==
| + | |
| − | | + | |
| − | ===Cat-call problem===
| + | |
| − | | + | |
| − | With covariant feature redefinition you run into cat-call problems as this example shows:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | food: FOOD
| + | |
| − | do
| + | |
| − | | + | |
| − | a := c
| + | |
| − | -- eat for type CAT takes arguments only of type CAT_FOOD
| + | |
| − | a.eat (food)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ===Restrict mechanism===
| + | |
| − | | + | |
| − | Using the restrict mechanism the default behavior for a type is to keep all features. Types which want to have covariant redefined features will not conform to the original type. You do not mention these features in a redefine clause.
| + | |
| − | | + | |
| − | {| class="top-aligned"
| + | |
| − | <e>
| + | |
| − | class CAT
| + | |
| − | | + | |
| − | inherit
| + | |
| − | | + | |
| − | ANIMAL
| + | |
| − | restrict eat end
| + | |
| − |
| + | |
| − | | + | |
| − | feature {NONE} -- Covariant redefinitions
| + | |
| − | | + | |
| − | eat (f: CAT_FOOD) is
| + | |
| − | -- Note that the feature must be exported to NONE - otherwise the compiler will complain.
| + | |
| − | do
| + | |
| − | ...
| + | |
| − | end
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | |}
| + | |
| − | | + | |
| − | Now the cat-call example with the new restrict types:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | do
| + | |
| − | -- illegal assignment, ANIMAL and CAT don't conform
| + | |
| − | -- since CAT inherits from ANIMAL restrict eat
| + | |
| − | a := c
| + | |
| − | a.eat (food)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ==Feature hiding==
| + | |
| − | | + | |
| − | ===Cat-call problem===
| + | |
| − | | + | |
| − | By restricting the export status of features, a cat-call problem is introduced:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | do
| + | |
| − | | + | |
| − | a := c
| + | |
| − | -- sleep is hidden for type CAT and should not be called
| + | |
| − | a.sleep
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ===Restricting the export status===
| + | |
| − | | + | |
| − | Types which reduce the export status of features will not conform to the original type.
| + | |
| − | | + | |
| − | | + | |
| − | Now the cat-call example:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL
| + | |
| − | c: CAT
| + | |
| − | do
| + | |
| − | -- illegal assignment, ANIMAL and CAT don't conform
| + | |
| − | a := c
| + | |
| − | a.sleep
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | a: ANIMAL export {NONE} sleep end
| + | |
| − | c: CAT
| + | |
| − | do
| + | |
| − | -- legal, CAT conforms to ANIMAL export {NONE} sleep
| + | |
| − | a := c
| + | |
| − | | + | |
| − | -- illegal, ANIMAL export {NONE} sleep doesn't export feature sleep
| + | |
| − | a.sleep
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | Note the declaration of a. It is desirable that any sort of inheritance restriction (such as listing creation procedures) be allowed on declarations.
| + | |
| − | | + | |
| − | ==Generics==
| + | |
| − | | + | |
| − | Conformance of generic classes (two generic classes conform if their base classes conform and their generic parameters conform) introduces another kind of covariantly redefined features. Every feature which has a generic argument, e.g. <e>put (g: G)</e> in class <e>LIST [G]</e> can be regarded as covariantly redefined since <e>put</e> in <e>LIST [ANY]</e> takes an argument of type <e>ANY</e> and <e>put</e> in <e>LIST [ANIMAL]</e> takes an argument of type <e>ANIMAL</e>. But <e>LIST [ANIMAL]</e> conforms to <e>LIST [ANY]</e>, thus the feature <e>put</e> is actually covariantly redefined in <e>LIST [ANIMAL]</e>.
| + | |
| − | | + | |
| − | ===Cat-call problem===
| + | |
| − | | + | |
| − | The conformance rules of generics also introduce cat-call problems:
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | -- feature `put' has argument type ANY
| + | |
| − | any_list: LIST [ANY]
| + | |
| − | -- feature `put' has argument type ANIMAL
| + | |
| − | animal_list: LIST [ANIMAL]
| + | |
| − | do
| + | |
| − | -- animal_list conforms to any_list, but arguments of the feature
| + | |
| − | -- `put' are different
| + | |
| − | any_list := animal_list
| + | |
| − | | + | |
| − | -- since any type can be put into any_list this will add
| + | |
| − | -- an integer to the animal_list
| + | |
| − | any_list.put (5)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | ===Conformance===
| + | |
| − | | + | |
| − | A generic class X [G] conforms to another generic class X [H] if G and H are the same class, or if G conforms to H and H does not export any features taking generic arguments, or any features that in turn (recursively) make use of features taking generic arguments.
| + | |
| − |
| + | |
| − | ===Using restrict mechanism===
| + | |
| − | | + | |
| − | <e>
| + | |
| − | local
| + | |
| − | any_list: LIST [ANY]
| + | |
| − | animal_list: LIST [ANIMAL]
| + | |
| − | do
| + | |
| − | -- illegal since animal_list does not conform to any_list anymore
| + | |
| − | any_list := animal_list
| + | |
| − | | + | |
| − | any_list.put (5)
| + | |
| − | | + | |
| − | end
| + | |
| − | </e>
| + | |
| − | | + | |
| − | <e>
| + | |
| − | | + | |
| − | local
| + | |
| − | any_list: like usable_list
| + | |
| − | animal_list: LIST [ANIMAL]
| + | |
| − | animal: ANIMAL
| + | |
| − | do
| + | |
| − | -- legal since LIST [ANIMAL] has `put'
| + | |
| − | animal_list.put (animal)
| + | |
| − | | + | |
| − | -- legal since animal_list conforms to `usable_list'
| + | |
| − | any_list := animal_list
| + | |
| − | | + | |
| − | -- illegal since `usable_list' does not have `put'
| + | |
| − | any_list.put (animal)
| + | |
| − | | + | |
| − | -- illegal, since `usable_list' does not have `has'
| + | |
| − | any_list.has (animal)
| + | |
| − | | + | |
| − | end
| + | |
| − | | + | |
| − | feature {NONE} -- Anchors
| + | |
| − | | + | |
| − | usable_list: LIST [ANY] restrict put, has, append, etc. etc. end
| + | |
| − | -- Anchor - in practice this anchor is defined in a class which is imported using non-conforming inheritance
| + | |
| − | </e>
| + | |
| − | | + | |
| − | --Colin Adams and Mark Howard
| + | |