Default and explicit variance
Research: This page describes research about Eiffel, not the actual language specification.
Contents
Introduction
This solution enables covariance and contravariance redefinition. The default behavior is detected automatically. When there is a cat-call risk the programmer must explicitly declare the variance.
Default variance
Feature redefinition
- Covariant redefinition of request result is allowed
- Contravariant redefinition of feature arguments is allowed
The first is a weaker precondition and the second is a stronger postconidtion. Only the second statment is a new semantic in Eiffel.
Generic conformance
Generic conformance is close from redefinition.
- Generic used only on feature argument is contravariant.
- Generic used only on request result or creation procedure (not included in constraint, unexported and unused in the class) is covariant.
- Generic used both on feature argument and request result is novariant.
And then genric not used is covariant and contravariant.
The first checkpoint "Generic lists" is checked. As explain for Usage-site variance the generic 'OPEN_ARGS' from ROUTINE, PROCEDURE, FUNCTION, PREDICATE must be contravariant and the generics of TUPLE must be covariants.
However 'OPEN_ARGS'is novariant. Indeed the genric type is used on request type and feature arguments.
The class must probably be redesigned. Indeed if contravariant is allowed then there may be a new contravariant cat-call on request result.
In these classes there is only two requests using the generic as type:
operands: detachable OPEN_ARGS empty_operands: OPEN_ARGS do create Result ensure ... end
The second request is not used internally and may be problematic for creation: How create a tuple of not self-initialized and attached types? The first is used in these features : 'target', 'is_equal', 'set_operands', 'copy', 'apply'
'is_equal', 'set_operands' and 'copy' rely on data model. 'apply' and 'target' are more sensitive. Indeed the problem is when there is an opened target. However a target must not be contravriant.
Maybe it is better to separate opened target and closed target in two abstractions. It is an opportunity to solve agent problems (see Minor-ECMA-problems). Abstraction for opened target would have then a generic 'OPEN_ARGS' novariant. It is really a problem? And more the syntax for agent with opened target suggest only target of one type is usable.
agent {SOME_TYPE}.a_routine
Another solution will be mentioned later.
Note: The generic 'RESULT_TYPE' of FUNCTION class is used only on request result. Then it is a covariant generic. It is a expected point.
Sub-conclusion
The default semantic use no new keyword. We can critisize the lack of explicity for generic conformance. But the variance for generic is can used.
Explicit variance (Variant typing)
What is the advantages of covariance compared to a novariant typing with an object test ?
- explicit and adaptative interface
- One routine
Feature redefinition
- Covariant redefinition of feature argument requires a 'variant' typing on redfined feature.
- Contravariant redefinition of request result is not allowed
class ANIMAL feature -- Access last: FOOD faeture -- Eating eat (f: like last) do last := f end end
class COW redefine all end feature -- Access last: GRASS faeture -- Eating eat (f: variant like last) -- or eat (f: variant FOOD) do if attached f as g then last := g end end end
'variant' typing requires a simple object test. Note that the type is not repeated in the object test. In the interface of the current type the 'variant' is removed. Indeed it is not possible to call 'eat' on a COW instance with a paramter of type FOOD.
Feature definition
- A feature argument can be declared variant for future covariant redefinition.
- A request type can be declared variant for future contravariant redefinition.
class ANIMAL feature -- Access last: FOOD faeture -- Eating eat (f: variant like last) do if attached f as safe then last := safe end end end
class COW redefine last end feature -- Access last: GRASS end
Contravariant redefinition of request result requires an object test on client side.
Then for agents it is possible to decalre requests 'operands' and 'empty_operands' as variant.
operands: variant detachable OPEN_ARGS empty_operands: variant OPEN_ARGS do create Result ensure ... end
But it is less practice. Indeed, if the object test is false in 'apply' or 'target' what to do? An abstraction separation is better. But this last can be a transitional solution.
Generic conformance
The variant typing can be used to change the default variance of generic type.
- Generic used only on feature argument or request result with variant typing is contravariant.
- Generic used only on request result or creation procedure (not included in constraint) or feature argument with variant typing is covariant.
Sub-conclusion
This proposition reused an existing keyword and is very close from the Detachable types proposition but without the detachable constraint. The contravariant redefinition of request result can be remoted for simplicty.
Other
Export status restrictions
Since ECMA forbiden the export restriction with conform inheritance is not a problem. However the semantic can be change for enable this restriction on conform inheritance.
Restrict exportation should not be cuased a catcall. The mechanism could be used just to change the class interface.
deferred class ANIMAL feature is_vegetarian: BOOLEAN deferred end end
class COW inherit ANIMAL export {NONE} is_vegetarian end feature {NONE} is_vegetarian: BOOLEAN = True end
local an_animal: ANIMAL; a_cow: COW b: BOOLEAN do create a_cow b := a_cow.is_vegetarian -- invalid call an_animal := a_cow b := an_animal .is_vegetarian -- valid call end
The class interface is easier.
Conclusion
The proposition use no new keyword and solve the cat-call problem. It passes all Catcall checkpoints.