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