Difference between revisions of "Without /except"

(Changed without to restrict, and a few other minor changes)
 
(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 06:10, 18 September 2007

Replaced by restrict