Difference between revisions of "Without /except"

 
(Changed without to restrict, and a few other minor changes)
Line 6: Line 6:
 
==Introduction==
 
==Introduction==
  
By allowing covariant feature redefinition and hiding of features, the Eiffel language introduces the problem of cat-calls (Changed Availability or Type). The forget / keep 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.
+
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.
 
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.
Line 112: Line 112:
 
===The idea===
 
===The idea===
  
To solve the problem of cat-calls, the idea is that whenever you covariantly redefine a feature of a parent type, you don't actually inherit this feature and redefine it, but you inherit from a (virtual) parent type which does not have this feature at all. The same goes for changing the export status. When you restrict the export status of a feature, you don't inherit this feature from the parent but introduce it in the current type and inherit from a parent which does not know this feature at all.
+
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> than 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>.
+
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>.
  
  
==Without mechanism==
+
==Restrict mechanism==
  
The without mechanism introduces a new keyword <e>without</e> which can be used to remove covariantly redefined features and features which change the export status from a base type. By removing features, a new type is introduced. The new type has all the features that the original type had, except those listed in the without clause. If <e>without 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 intoduce another new keyword <e>except</e> which can only follow <e>without all</e> (therefore it does not need to be a compeletly reserved word).
+
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===
 
===Default behavior===
  
In the without 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 change an export status or covariantly redefine a feature don't conform to this type anymore.
+
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>
 
<e>
Line 139: Line 139:
 
     -- illegal since CAT does not conform to ANIMAL anymore.
 
     -- illegal since CAT does not conform to ANIMAL anymore.
 
     -- N.B. The declaration of CAT given above is illegal under this proposal.  
 
     -- N.B. The declaration of CAT given above is illegal under this proposal.  
     -- Adding the without keyword will make it legal again, and emphasize the lack of conformance.
+
     -- Adding the restrict keyword will make it legal again, and emphasize the lack of conformance.
 
   animal := cat
 
   animal := cat
 
end
 
end
Line 151: Line 151:
 
a1: ANIMAL
 
a1: ANIMAL
  
   -- a type which lacks all features, thus all  
+
   -- a type which lacks any exported features, thus all  
   -- subtypes conform to this type. It is not a useful type.
+
   -- subtypes conform to this type. It is not a very useful type.
a2: ANIMAL without all end
+
a2: ANIMAL restrict all end
  
   -- a type which only lacks the features `eat' and `sleep'. All subtypes which only redefine or change export status  
+
   -- 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
 
   -- of `eat' or `sleep' will conform to this type
a3: ANIMAL without eat, sleep end
+
a3: ANIMAL restrict eat export {NONE} sleep end
  
  -- a type which lacks all features other than eat.
+
  -- a type which lacks any exported features other than eat.
a4: ANIMAL without all except eat end
+
a4: ANIMAL restrict all except eat end
  
 
</e>
 
</e>
Line 185: Line 185:
 
</e>
 
</e>
  
===Without mechanism===
+
===Restrict mechanism===
  
Using the without 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. In fact, under this scheme, you do not "redefine" features covariantly at all. As you are not inheriting them from the original type (specified by the without keyword), you simply introduce them in the class text. You do not mention them in a redefine clause.
+
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"
 
{| class="top-aligned"
Line 196: Line 196:
  
 
   ANIMAL  
 
   ANIMAL  
     without eat end
+
     restrict eat end
 
    
 
    
  
feature
+
feature {NONE} -- Covariant redefinitions
  
   eat (f: CAT_FOOD)
+
   eat (f: CAT_FOOD) is
 +
    -- Note that the feature must be exported to NONE - otherwise the compiler will complain.
 +
  do
 +
    ...
 +
  end
  
 
end
 
end
Line 207: Line 211:
 
|}
 
|}
  
Now the cat-call example with the new without types:
+
Now the cat-call example with the new restrict types:
  
 
<e>
 
<e>
Line 215: Line 219:
 
do
 
do
 
     -- illegal assignment, ANIMAL and CAT don't conform
 
     -- illegal assignment, ANIMAL and CAT don't conform
     -- since CAT inherits from ANIMAL without eat
+
     -- since CAT inherits from ANIMAL restrict eat
 
   a := c
 
   a := c
 
   a.eat (food)
 
   a.eat (food)
Line 241: Line 245:
 
</e>
 
</e>
  
===Using without mechanism===
+
===Restricting the export status===
  
Using the without mechanism the default behavior for a type is to keep all features. Types which change the export status of features will not conform to the original type. This is not allowed. Instead you use the without mechanism to remove these features from the type you are creating, then introduce them afresh in an export {NONE} feature clause, like any other new feature.
+
Types which reduce the export status of features will not conform to the original type.  
  
{| class="top-aligned"
 
|-
 
! the old way
 
! the new way
 
|-
 
|
 
<e>
 
class CAT
 
 
inherit
 
 
  ANIMAL
 
    export
 
      {NONE} sleep
 
    end
 
 
end
 
</e>
 
|
 
<e>
 
class CAT
 
 
inherit
 
 
  ANIMAL
 
    without sleep end
 
 
feature {NONE} -- Implementation
 
   
 
  sleep is
 
    -- ...
 
  do
 
    ...
 
  end
 
 
end
 
</e>
 
|}
 
  
 
Now the cat-call example:
 
Now the cat-call example:
Line 300: Line 266:
 
<e>
 
<e>
 
local
 
local
   a: ANIMAL without all except eat end
+
   a: ANIMAL export {NONE} sleep end
 
   c: CAT
 
   c: CAT
 
do
 
do
     -- legal, CAT conforms to ANIMAL without all except eat
+
     -- legal, CAT conforms to ANIMAL export {NONE} sleep
 
   a := c
 
   a := c
  
     -- illegal, ANIMAL without all except eat doesn't have a feature sleep
+
     -- illegal, ANIMAL export {NONE} sleep doesn't export feature sleep
 
   a.sleep
 
   a.sleep
  
Line 312: Line 278:
 
</e>
 
</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==
 
==Generics==
Line 341: Line 308:
 
===Conformance===
 
===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 lacks any features taking generic arguments.
+
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 without mechanism===
+
===Using restrict mechanism===
  
 
<e>
 
<e>
Line 381: Line 348:
 
feature {NONE} -- Anchors
 
feature {NONE} -- Anchors
  
   usable_list: LIST [ANY] without put, has, append, etc. etc. end
+
   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
 
       -- Anchor - in practice this anchor is defined in a class which is imported using non-conforming inheritance
 
</e>
 
</e>
  
 
--Colin Adams and Mark Howard
 
--Colin Adams and Mark Howard

Revision as of 01:54, 18 September 2007

Research: This page describes research about Eiffel, not the actual language specification.

Construction.png Not Ready for Review: This Page is Under Development!

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 ANIMAL
 
feature
 
  eat (f: FOOD)
 
  sleep
 
end
class CAT
 
inherit
 
  ANIMAL
    redefine
      eat
    export
      {NONE} sleep
    end
 
feature
 
  eat (f: CAT_FOOD)
 
end
class FOOD
 
end
 
 
class CAT_FOOD
 
inherit
 
  FOOD
 
end
class LIST [G]
 
feature
 
  has (g: G): BOOLEAN
 
  put (g: G)
 
  item: G
 
end

The problem

The problem of cat-calls has two variants, one because a feature is covariantly redefined:

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

And one because the visibility of features change:

local
  a: ANIMAL
  c: CAT
do
 
  a := c
    -- `sleep' is hidden for type CAT and should not be called
  a.sleep
 
end

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 CAT inherits from the type ANIMAL but covariantly redefines the feature eat then the type CAT does not conform to ANIMAL anymore, but only to a type ANIMAL without the feature eat.


Restrict mechanism

The restrict mechanism introduces a new keyword restrict 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 restrict all 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 except which can only follow restrict all (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.

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

Syntax

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


Covariant feature redefinition

Cat-call problem

With covariant feature redefinition you run into cat-call problems as this example shows:

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

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

Now the cat-call example with the new restrict types:

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

Feature hiding

Cat-call problem

By restricting the export status of features, a cat-call problem is introduced:

local
  a: ANIMAL
  c: CAT
do
 
  a := c
    -- sleep is hidden for type CAT and should not be called
  a.sleep
 
end

Restricting the export status

Types which reduce the export status of features will not conform to the original type.


Now the cat-call example:

local
  a: ANIMAL
  c: CAT
do
    -- illegal assignment, ANIMAL and CAT don't conform
   a := c
  a.sleep
 
end
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

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. put (g: G) in class LIST [G] can be regarded as covariantly redefined since put in LIST [ANY] takes an argument of type ANY and put in LIST [ANIMAL] takes an argument of type ANIMAL. But LIST [ANIMAL] conforms to LIST [ANY], thus the feature put is actually covariantly redefined in LIST [ANIMAL].

Cat-call problem

The conformance rules of generics also introduce cat-call problems:

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

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

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

--Colin Adams and Mark Howard