|
|
| (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
| |