Without /except: Difference between revisions

From EiffelStudio: an EiffelSoftware project
Jump to navigation Jump to search
Changed without to restrict, and a few other minor changes
No edit summary
 
(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

Latest revision as of 15:10, 18 September 2007

Replaced by restrict