Forget / keep
Contents
Introduction
The forget / keep mechanism enhances the type system. It allows to change types on the feature level by forgetting specific features (especially features which are covariantly redefined) so that these features can't be called anymore. It also allows to specifically keep certain features so that they are surely available which in turn changes the conformance rules to only allow types which have this feature with the exact same signature.
Syntax
-- forget types -- A type which forgets all covariantly redefined features, thus all -- subtypes conform to this type. a: ANIMAL forget all end -- A type which only forgets the features `eat' and `sleep' but not other -- covarianlty redefined features or features whose export status is -- restricted. All subtypes which only redefine or restrict export status -- of `eat' or `sleep' will conform to this type. a: ANIMAL forget eat, sleep end -- keep types -- A type which keeps all features and looses conformance from subtypes which -- covariantly redefine features or restrict export status. b: ANIMAL keep all end -- A type where all subtypes conform except those who covariantly redefine -- feature `eat'. b: ANIMAL keep eat end
Semantics
The meaning of a forget clause is that the actual type which is used does not have the features listed in the forget clause. forget all
is a bit misleading as it does not forget all features, but only features which are covariantly redefined or have the export status changed to be more restrictive later in the inheritance hierarchy. A better naming would be something along the lines of forget covariance
but this does not address the export status change.
The meaning of a keep clause is the reverse. If a feature is listed in a keep clause it can be called on that type with the arguments listed in that type. This means in turn that all subclasses which change the signature of those features covariantly don't conform anymore as a call would not be safe anymore.
Default behavior
The mechanism has two possible default behaviors. If a type is declared without specifying which features to keep or to forget, it can either mean a type which keeps all features or a type which forgets all features. The default behavior has a big impact on the language:
- In the keep all case, all subclasses which have a covariantly redefined feature don't conform to the parent type anymore as it keeps this feature.
- In the forget all case, all subclasses conform to the parent per default, but all features which are covariantly redefined (even much later in the inheritance hierarchy) are not callable since they are forgotten.
Conformance rules
Conformance is best explained on the feature level. A type SUB
conforms to a type PARENT
if all features which are available in PARENT
are also available in SUB
. You have to keep in mind that the type PARENT
can forget certain features. Thus SUB
only conforms to the type PARENT forget x
if SUB also has a forget x
clause. For the keep clause it is a little bit different: A type SUB
only conforms to the type PARENT keep x
if SUB
does not restrict the export status of x
or changes the arguments covariantly, thus it has the same interface regarding feature x
.
Examples
See the introduction to examples for information about the classes used.
Cats and Dogs
The examples are taken from the reference article
forget mechanism
The example regarding covariance on the feature level with the forget mechanism looks as follows:
local a: ANIMAL -- means ANIMAL keep all c: CAT do -- illegal assignment, ANIMAL and CAT don't conform -- since CAT does not have the same signature for `eat' a := c a.eat (food) end
local a: ANIMAL forget all end c: CAT do -- legal, CAT conforms to ANIMAL forget all a := c -- illegal, ANIMAL forget all doesn't have a feature eat a.eat (food) end
keep mechanism
The example regarding covariance on the feature level with the keep mechanism looks as follows:
local a: ANIMAL -- means ANIMAL forget all c: CAT do a := c -- illegal call since the type ANIMAL does not have a feature `eat' anymore a.eat (food) end
local a: ANIMAL keep all end c: CAT do -- illegal, CAT does not conform to ANIMAL keep all a := c -- legal, ANIMAL keep all still has the feature `eat' a.eat (food) end
A Simple Generic Algorithm
deferred class PERSON_PRINTER feature print_attributes (a_list: LIST [PERSON] forget all) -- Print all attributes of each person in the list. deferred end end deferred class EXAMPLE feature example -- Shows example usage of PERSON_PRINTER. local l_students: LINKED_LIST [STUDENT] l_professors: LINKED_LIST [PROFESSOR] l_person_printer: PERSON_PRINTER do create l_person_printer l_person_printer.print_attributes (l_students) l_person_printer.print_attributes (l_professor) end end
By forgetting all covariantly redefined features the generics conform and we can reuse this algorithm for all implementations of LIST
of descendants of PERSON
.
Conclusion
This proposal solves CAT calls as a whole. This include CAT calls which are introduced by covariant argument redefinition through formal arguments in generic classes. It is not as expressive as other solutions (not applicable to agents, comparator example) and it is likely that it adds a lot of syntax to the code.