|
|
(One intermediate revision 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, your new classes conforms to a virtual class of the same type as the parent type except that it lacks the covariant feature. The same goes for changing the export status of a feature.
| + | |
− | | + | |
− | For instance, 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 are not part of the conforming type. If <e>restrict all</e> is used, all features in the original will be removed from the new conforming type. This produces a conforming type with no features at all. We also 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 (that is, it conforms to the type). 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 in the conforming type
| + | |
− | 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 emphasizes 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 conforming features, thus all
| + | |
− | -- subtypes conform to this type.
| + | |
− | a2: ANIMAL restrict all end
| + | |
− | | + | |
− | -- a type which only restricts the features `eat' and `sleep'. All subtypes which only redefine (but not restrict) `eat' or change the export status
| + | |
− | -- of `sleep' will conform to this type.
| + | |
− | a3: ANIMAL restrict eat export {NONE} sleep end
| + | |
− | | + | |
− | -- subtypes of the following type conform to it provided they do not restrict (that is, covariantly redefine) `eat' (or restrict its export status).
| + | |
− | 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 conform to its parent with respect to 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, but in a restrict clause.
| + | |
− | | + | |
− | {| class="top-aligned"
| + | |
− | <e>
| + | |
− | class CAT
| + | |
− | | + | |
− | inherit
| + | |
− | | + | |
− | ANIMAL
| + | |
− | restrict eat end
| + | |
− |
| + | |
− | | + | |
− | feature -- Covariant redefinitions
| + | |
− | | + | |
− | eat (f: CAT_FOOD) is
| + | |
− | 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 (and therefore conforms to) 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's conforming type does not include 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 readable_list
| + | |
− | animal_list: LIST [ANIMAL]
| + | |
− | animal: ANIMAL
| + | |
− | do
| + | |
− | -- legal since LIST [ANIMAL] has `put'
| + | |
− | animal_list.put (animal)
| + | |
− | | + | |
− | -- legal since animal_list conforms to `readable_list'
| + | |
− | any_list := animal_list
| + | |
− | | + | |
− | -- illegal since `readable_list' does not have `put'
| + | |
− | any_list.put (animal)
| + | |
− | | + | |
− | -- illegal, since `readable_list' does not have `has'
| + | |
− | any_list.has (animal)
| + | |
− | | + | |
− | end
| + | |
− | | + | |
− | feature {NONE} -- Anchors
| + | |
− | | + | |
− | readable_list: LIST [ANY] restrict put, has end
| + | |
− | -- Anchor - in practice this anchor is defined in a class which is imported using non-conforming inheritance
| + | |
− | </e>
| + | |
− | | + | |
− | --Colin Adams and Mark Howard
| + | |