Difference between revisions of "Default and explicit variance"

m (remove ambigious negation)
m (Discard the extaended variant typing)
 
(47 intermediate revisions by 2 users not shown)
Line 2: Line 2:
 
[[Category:Catcall]]
 
[[Category:Catcall]]
 
{{Research}}
 
{{Research}}
=== Introduction ===
+
== Introduction ==
This solution enables covariance and contravariance redefinition.
+
In first the variant typing is exposed. It checks all catcall checkpoints.
The default behavior is detected automatically. When there is a cat-call risk the programmer must explicitly declare the variance.
+
In second the inroduced mechanism is compared against other.
  
== Default variance ==
+
== Variant typing ==
=== 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.
+
 
+
* Contravariant redefinition of request result is allowed for non-conforming inheritance
+
* Covariant redefinition of feature arguments is allowed for non-conforming inheritance
+
 
+
=== Generic conformance ===
+
Generic conformance is close from redefinition.
+
 
+
* Generic used only on feature argument is contravariant.
+
* Generic used only on request result is covariant.
+
* Generic used both on feature argument and request result is novariant.
+
 
+
And then generic 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 generic 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:
+
<e>
+
operands: detachable OPEN_ARGS
+
 
+
empty_operands: OPEN_ARGS
+
do create Result ensure ... end
+
</e>
+
 
+
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.
+
 
+
'apply' could be restricted for no opened arguments.
+
<e>
+
apply
+
require
+
no_opened_operands: open_count = 0
+
do
+
call (Void)
+
end
+
</e>
+
'target' could be modfied:
+
<e>
+
target: ANY
+
require
+
is_target_closed
+
do
+
-- ...
+
end
+
</e>
+
 
+
Or maybe it is better to separate opened target and closed target in two abstractions.
+
 
+
The redisgn of agent classes is an opportunity to solve agent problems (see [[Minor-ECMA-problems]], [[Agents in SCOOP]]).
+
 
+
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 an 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 little used.
+
 
+
The addition in TYPE class for reflexivity and dynamic object test is little.
+
Indeed two simple booleans "is_contravariant" and "is_covariant" for each generic is required.
+
<e>
+
generic_parameter_contravariant (i: INTEGER): BOOLEAN
+
-- Is `i'-th generic parameter contravariant?
+
+
generic_parameter_covariant (i: INTEGER): BOOLEAN
+
-- Is `i'-th generic parameter covariant?
+
</e>
+
 
+
== Explicit variance (Variant typing) ==
+
 
What is the advantages of covariance compared to a novariant typing?
 
What is the advantages of covariance compared to a novariant typing?
 
* explicit and adaptative interface
 
* explicit and adaptative interface
 
* One routine
 
* One routine
 +
 +
=== Variant typing ===
 +
* A variant entity has '''the most restrictive type'''.
 +
* A variant entity requires a simple object test to use the entity with the '''expected type'''.
 +
 +
Examples are available in next sections.
  
 
=== Feature redefinition ===
 
=== Feature redefinition ===
These rules concern the conforming inheritance.
+
* Covariant redefinition of request result is allowed (as currently).
* Covariant redefinition of feature argument requires a 'variant' typing on redfined feature or first feature definition.
+
* Covariant redefinition of feature argument requires a variant typing on redefined argument or first definition.
* Contravariant redefinition of request result requires a 'variant' typing on previous feature definition.
+
'''The most restrictive type''' for a variant type argument is the type of the first definition.
  
A 'variant' typing requires a simple object test to use the entity with the expected type.
+
The first rule is a stronger postcondition. The current semantic is not changed.
  
'''example 1:''' covariant redefinition of feature argument with 'variant' typing on redfined feature
+
'''example 1:''' covariant redefinition of feature argument with 'variant' typing on redefined argument
  
Note that the type is not repeated in the object test.
+
Note:
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.
+
* The object test is not needed.
 +
* the type is not repeated in the object test.
 +
* In the class interface the 'variant' is removed. Indeed, it is not possible to call 'eat' on an entity of type COW with a parameter of type FOOD.
  
 
<e>
 
<e>
Line 114: Line 38:
 
last: FOOD
 
last: FOOD
  
faeture -- Eating
+
feature -- Eating
 
eat (f: like last)
 
eat (f: like last)
 
require
 
require
Line 125: Line 49:
 
end
 
end
 
</e>
 
</e>
 
 
<e>
 
<e>
 
class
 
class
Line 137: Line 60:
 
last: GRASS
 
last: GRASS
  
faeture -- Eating
+
feature -- Eating
eat (f: variant like last) -- or eat (f: variant FOOD)
+
eat (f: variant like last) -- or eat (f: variant GRASS)
 
require else
 
require else
 
True
 
True
-- 'f' type = type of the feature argument. Here: GRASS
+
-- 'f' type = expected type. Here: GRASS
 
do
 
do
-- 'f' type = type of the first feature definition. Here: FOOD
+
-- 'f' type = type of the first definition. Here: FOOD
if attached f as g then
+
if attached f as expected then
-- 'g' type = type of the feature argument. Here: GRASS
+
-- 'expected' type = expected type. Here: GRASS
last := g
+
last := expected
 
end
 
end
 
ensure then
 
ensure then
 
True
 
True
-- 'f' type = type of the first feature definition. Here: FOOD
+
-- 'f' type = type of the first definition. Here: FOOD
 
end
 
end
  
Line 164: Line 87:
 
last: FOOD
 
last: FOOD
  
faeture -- Eating
+
feature -- Eating
 
eat (f: variant like last)
 
eat (f: variant like last)
 
do
 
do
if attached f as safe then
+
if attached f as expected then
last := safe
+
last := expected
 
end
 
end
 
end
 
end
Line 188: Line 111:
 
</e>
 
</e>
  
'''example 3:''' contravariant redefinition
+
=== Generic conformance ===
 +
Default: a generic is novariant
  
Z inherits of Y
+
If a genric must be variant (covariant or contravariant or both) then the formal generic must be prefixed with the 'variant' mark.
  
Y inherits of X
+
The compiler must ensure that the variant generic checks one next rule:
 +
* Generic used only on feature argument is contravariant.
 +
* Generic used only on request result or feature argument with variant typing is covariant.
 +
* Generic not used is both contravariant and covariant.
 +
 
 +
'''The most restrictive type''' for a variant generic argument is the constrained inheritance type.
 +
'''
 +
example:'''
 
<e>
 
<e>
class
+
deferred class
A
+
EXAMPLE [variant K, variant G]
 +
-- K is contravariant and G is covariant
  
 
feature -- Access
 
feature -- Access
something: variant Z
+
first: G
 
 
feature -- Other
+
item (i: K): G
do_something
+
deferred
do
+
-- 'something' type = ANY
+
if attached something as expected then
+
-- 'expected' type = feature result type. Here: Z
+
end
+
 
end
 
end
+
 
 
end
 
end
 
</e>
 
</e>
<e>
 
class
 
B
 
 
inherit
 
A
 
redefine something end
 
  
feature -- Access
+
Therefore the next code is valid:
something: variant Y
+
<e>
+
local
 +
a: EXAMPLE [INTEGER, COMPARABLE]; b: EXAMPLE [NUMERIC, STRING]
 +
do
 +
-- ...
 +
a := b
 
end
 
end
 
</e>
 
</e>
 +
 +
==== Agent conformance ====
 +
As explained for [[Usage-site variance]] the generic 'OPEN_ARGS' from ROUTINE, PROCEDURE, FUNCTION, PREDICATE must be contravariant.
 +
 
<e>
 
<e>
 
class
 
class
C
+
ROUTINE [BASE_TYPE, variant OPEN_ARGS -> detachable TUPLE create default_create end]
+
-- ...
inherit
+
end
B
+
</e>
redefine something, do_something end
+
  
feature -- Access
+
The generic 'OPEN_ARGS' is used on feature arguments, but also on request result.
something: X
+
  
feature -- Other
+
The class should probably be redesigned. Indeed if contravariant is allowed then there may be a new contravariant catcall on request result.
do_something
+
 
do
+
In these classes there are only two requests using the generic as type:
-- 'something' type = X
+
<e>
end
+
operands: detachable OPEN_ARGS
end
+
 
 +
empty_operands: OPEN_ARGS
 +
do create Result ensure ... end
 
</e>
 
</e>
There is little interests to use contravariance on non-generic. It is more interesting for constrained generics.
 
  
=== Generic conformance ===
+
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 variant typing can be used to change the default variance of generic type.
+
The first is used in these features : 'target', 'is_equal', 'set_operands', 'copy', 'apply'
  
* Generic used only on feature argument or request result with variant typing is contravariant.
+
'is_equal', 'set_operands' and 'copy' and 'target' rely on data model.
* Generic used only on request result or feature argument with variant typing is covariant.
+
'apply' is more sensitive. Indeed the problem is when there is an opened target.
  
 +
'apply' could be restricted for no opened arguments or could be removed since it is not a lot used.
 +
<e>
 +
apply
 +
require
 +
no_operands: open_count = 0
 +
do
 +
call (Void)
 +
end
 +
</e>
  
 +
The redesign of agent classes is an opportunity to solve agent problems (see [[Minor-ECMA-problems]], [[Agents in SCOOP]]).
  
example: agents
+
Another solution will be mentioned later.
Then for agents it is possible to decalre requests 'operands' and 'empty_operands' as variant.
+
 
 +
Note:
 +
The generic 'RESULT_TYPE' of FUNCTION class is used only on request result. Then it is a covariant generic. It is an expected point.
 +
 
 +
=== Comparison with other solutions ===
 +
==== [[Detachable types]] (non-generic) ====
 +
The variant typing is not in conflict with the void-safe typing.
 +
 
 +
And more the object test is not needed.
 +
 
 +
==== [[Usage-site variance]] (generic) ====
 +
The variant typing is a supplier specification while usage-site variance is a client specification.
 +
 
 +
Another difference is the interface restriction of the usage-site variance. With the variant typing for generics the interface is fully aivailable.
 +
 
 +
=== Sub-conclusion ===
 +
The limited variant typing is reasonable and expressive. It limits Eiffel changes and solves all catcall problems.
 +
 
 +
The proposal reuses an existing keyword.
 +
 
 +
The addition in TYPE class for reflexivity and dynamic object test is little.
 +
Indeed two simple booleans "is_contravariant" and "is_covariant" for each generic is required.
 
<e>
 
<e>
operands: variant detachable OPEN_ARGS
+
generic_parameter_contravariant (i: INTEGER): BOOLEAN
 +
-- Is `i'-th generic parameter contravariant?
 +
 +
generic_parameter_covariant (i: INTEGER): BOOLEAN
 +
-- Is `i'-th generic parameter covariant?
 +
</e>
  
empty_operands: variant OPEN_ARGS
+
A possible critical could be the generic conformance restriction. Propositions are mentioned below.
do create Result ensure ... end
+
 
 +
== General discussions ==
 +
=== Greater flexibility for generics ===
 +
A lot of generics could be novariant, encouraging to propose a solution to have a new flexibility, but safe.
 +
==== Wildcard generics ====
 +
The request result type is the constrained inheritance type. And the type of the feature argument is (attached) NONE.
 +
The new semantic of 'Void' is considered: Void is not a NONE instance.
 +
 
 +
<e>
 +
local
 +
a: ARRAY [?]; b: ARRAY [STRING]
 +
o: ANY
 +
do
 +
-- ...
 +
a := b
 +
o := a.item (1)
 +
a.put ("try") -- invalid call. The type expected is NONE.
 +
end
 
</e>
 
</e>
  
'apply' and 'target' should be write again. For example:
+
However the wildcard generics will be used on request result or feature argument. A more power and elegent solution could be the parametrized routines.
 +
 
 +
==== Parametrized routines ====
 
<e>
 
<e>
apply
+
do_something [G] (a: ARRAY [G]): G
 +
require
 +
a.count >= 1
 
do
 
do
-- 'operands' type = constraint inheritance. Here: detachable TUPLE
+
Result := a.item (1)
if attached operands as args then
+
end
-- 'operands' type = request result type. Here: OPEN_ARGS
+
</e>
call (args)
+
 
else
+
==== Right abstraction ====
call (Void)
+
The wildcard generics and the parametrized routines introduce new constructs for Eiffel. Is there another solution avoiding this?
 +
 
 +
With a right abstraction it is possible to have flexible classes. It is comparable to the imuutable cocncept.
 +
 
 +
'''example:''' V_CONTAINER class of Eiffel Base 2
 +
To obtain the genric covariance behavior it is necessary to have:
 +
* V_ITERATOR must have a covariant generic.
 +
* TUPLE should be a read-only interface (V_MUTABLE_TUPLE would be the current TUPLE class). Therefore TUPLE could be conformed to V_SEQUENCE and V_MUTABLE_TUPLE conformed to V_ARRAY. Note that only the readonly interface could be in the Eiffel Standard.
 +
 
 +
In V_CONTAINER class only two features should use the variant typing:
 +
<e>
 +
new_cursor: V_ITERATOR [G]
 +
do ... end
 +
 
 +
occurrences (v: variant G): INTEGER
 +
do
 +
-- G -> ANY then 'v' type = ANY
 +
across Current as it loop
 +
if it.item = v then
 +
Result := Result + 1
 +
end
 +
end
 
end
 
end
 +
 +
has (v: variant G)
 +
local
 +
it: like new_cursor
 +
do
 +
-- G -> ANY then 'v' type = ANY
 +
it := new_cursor -- 'it' type = V_ITERATOR [ANY]
 +
it.search_forth (v)
 +
Result := not it.after
 +
ensure
 +
occurrences (v) = 1
 
end
 
end
 
</e>
 
</e>
  
=== Sub-conclusion ===
+
In V_ITERATOR only two features should use the variant typing without object test!
This proposition reused an existing keyword and is very close from the [[Detachable types]] proposition but without the detachable constraint and extended on the generics.
+
 
The contravariant redefinition of request result can be remoted for simplicty.
+
With these changes the next code is valid:
 +
<e>
 +
local
 +
a: V_CONTAINER [ANY]; b: V_CONTAINER [STRING]
 +
do
 +
a := b
 +
 +
end
 +
</e>
 +
 
 +
==== Sub-conclusion ====
 +
With a right abstraction and a smart use of variant typing for generics, it is possible to obtain a greater flexibility keeping a fully aivailable interface.
 +
 
 +
=== Limit changes ===
 +
To limit code change and to increase typing consistency, we can change the semantic of 'variant'.
 +
 
 +
Contravariant redefinition of request result is not very useful.
 +
The variant typing could be limited for covariant redefinition. There could have no effect on request result.
 +
 
 +
For generic conformance, a formal generic prefixed with the variant mark is covariant. The variant mark is not required on feature argument limiting code change.
 +
 
 +
However we have a problem: How mark a contravariant generic? There is three options:
 +
* Another mark.
 +
* 'frozen' mark for novariant generic and then no mark for contravariant generic: too restrictive since novariance concerns a lot of generic classes
 +
* No prefixed mark means novariant or contravariant generic
  
== Other ==
 
 
=== Export status restrictions ===
 
=== Export status restrictions ===
 
Since the ECMA Eiffel Standard forbids the export restriction with conforming inheritance, it is not a problem.
 
Since the ECMA Eiffel Standard forbids the export restriction with conforming inheritance, it is not a problem.
However the semantic can be changed for enable this restriction on conforming inheritance.
+
However, the semantic can be changed to enable this restriction on conforming inheritance.
  
Restrict exportation should not cuase a cat-call. The mechanism could be used just to change the class interface.
+
Restrict exportation should not cause a catcall. The mechanism could be used just to change the class interface.
 
<e>
 
<e>
 
deferred class
 
deferred class
Line 318: Line 357:
 
</e>
 
</e>
  
The class interface is easier.
+
The class interface is more simple and readable.
  
 
== Conclusion ==
 
== Conclusion ==
The proposition use no new keyword and solve the cat-call problem.
+
The proposition uses no new keyword and solves the catcall problem.
 
It passes all [[Catcall checkpoints]].
 
It passes all [[Catcall checkpoints]].
  
The default generic conformance give a natural safe and flexible static typing.
+
The new generic conformance gives a natural, safe, and flexible static typing.
The variant typing enables to create adaptive interfaces keeping a safe static typing.
+
The variant typing allows to create adaptive interfaces keeping a safe static typing.
 +
 
 +
With a fine abstraction and the use of variant typing for generics, the genric conformance flexibility is little restricted.
  
 
There is no interface restriction ([[Interval types]] or [[Usage-site variance]]). Class interfaces are fully available.
 
There is no interface restriction ([[Interval types]] or [[Usage-site variance]]). Class interfaces are fully available.
 +
 +
== Your view ==
 +
name:
 +
comment:
 +
 +
...

Latest revision as of 09:09, 11 May 2014

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

Introduction

In first the variant typing is exposed. It checks all catcall checkpoints. In second the inroduced mechanism is compared against other.

Variant typing

What is the advantages of covariance compared to a novariant typing?

  • explicit and adaptative interface
  • One routine

Variant typing

  • A variant entity has the most restrictive type.
  • A variant entity requires a simple object test to use the entity with the expected type.

Examples are available in next sections.

Feature redefinition

  • Covariant redefinition of request result is allowed (as currently).
  • Covariant redefinition of feature argument requires a variant typing on redefined argument or first definition.

The most restrictive type for a variant type argument is the type of the first definition.

The first rule is a stronger postcondition. The current semantic is not changed.

example 1: covariant redefinition of feature argument with 'variant' typing on redefined argument

Note:

  • The object test is not needed.
  • the type is not repeated in the object test.
  • In the class interface the 'variant' is removed. Indeed, it is not possible to call 'eat' on an entity of type COW with a parameter of type FOOD.
class
	ANIMAL
 
feature -- Access
	last: FOOD
 
feature -- Eating
	eat (f: like last)
		require
			True
		do
			last := f
		ensure
			True
		end
end
class
	COW
 
inherit
	ANIMAL
		redefine all end
 
feature -- Access
	last: GRASS
 
feature -- Eating
	eat (f: variant like last) -- or eat (f: variant GRASS)
		require else
			True
			-- 'f' type = expected type. Here: GRASS
		do
			-- 'f' type = type of the first definition. Here: FOOD
			if attached f as expected then
				-- 'expected' type = expected type. Here: GRASS
				last := expected
			end
		ensure then
			True
			-- 'f' type = type of the first definition. Here: FOOD
		end
 
end

example 2: covariant redefinition of feature argument with 'variant' typing on first feature definition.

class
	ANIMAL
 
feature -- Access
	last: FOOD
 
feature -- Eating
	eat (f: variant like last)
		do
			if attached f as expected then
				last := expected
			end
		end
end
class
	COW
 
inherit
	ANIMAL
		redefine last end
 
feature -- Access
	last: GRASS
 
end

Generic conformance

Default: a generic is novariant

If a genric must be variant (covariant or contravariant or both) then the formal generic must be prefixed with the 'variant' mark.

The compiler must ensure that the variant generic checks one next rule:

  • Generic used only on feature argument is contravariant.
  • Generic used only on request result or feature argument with variant typing is covariant.
  • Generic not used is both contravariant and covariant.

The most restrictive type for a variant generic argument is the constrained inheritance type. example:

deferred class
	EXAMPLE [variant K, variant G]
		-- K is contravariant and G is covariant
 
feature -- Access
	first: G
 
	item (i: K): G
		deferred
		end
 
end

Therefore the next code is valid:

local
	a: EXAMPLE [INTEGER, COMPARABLE]; b: EXAMPLE [NUMERIC, STRING]
do
	-- ...
	a := b
end

Agent conformance

As explained for Usage-site variance the generic 'OPEN_ARGS' from ROUTINE, PROCEDURE, FUNCTION, PREDICATE must be contravariant.

class
	ROUTINE [BASE_TYPE, variant OPEN_ARGS -> detachable TUPLE create default_create end]
-- ...
end

The generic 'OPEN_ARGS' is used on feature arguments, but also on request result.

The class should probably be redesigned. Indeed if contravariant is allowed then there may be a new contravariant catcall on request result.

In these classes there are 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' and 'target' rely on data model. 'apply' is more sensitive. Indeed the problem is when there is an opened target.

'apply' could be restricted for no opened arguments or could be removed since it is not a lot used.

apply
	require
		no_operands: open_count = 0
	do
		call (Void)
	end

The redesign of agent classes is an opportunity to solve agent problems (see Minor-ECMA-problems, Agents in SCOOP).

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 an expected point.

Comparison with other solutions

Detachable types (non-generic)

The variant typing is not in conflict with the void-safe typing.

And more the object test is not needed.

Usage-site variance (generic)

The variant typing is a supplier specification while usage-site variance is a client specification.

Another difference is the interface restriction of the usage-site variance. With the variant typing for generics the interface is fully aivailable.

Sub-conclusion

The limited variant typing is reasonable and expressive. It limits Eiffel changes and solves all catcall problems.

The proposal reuses an existing keyword.

The addition in TYPE class for reflexivity and dynamic object test is little. Indeed two simple booleans "is_contravariant" and "is_covariant" for each generic is required.

generic_parameter_contravariant (i: INTEGER): BOOLEAN
	-- Is `i'-th generic parameter contravariant?
 
generic_parameter_covariant (i: INTEGER): BOOLEAN
	-- Is `i'-th generic parameter covariant?

A possible critical could be the generic conformance restriction. Propositions are mentioned below.

General discussions

Greater flexibility for generics

A lot of generics could be novariant, encouraging to propose a solution to have a new flexibility, but safe.

Wildcard generics

The request result type is the constrained inheritance type. And the type of the feature argument is (attached) NONE. The new semantic of 'Void' is considered: Void is not a NONE instance.

local
	a: ARRAY [?]; b: ARRAY [STRING]
	o: ANY
do
	-- ...
	a := b
	o := a.item (1)
	a.put ("try") -- invalid call. The type expected is NONE.
end

However the wildcard generics will be used on request result or feature argument. A more power and elegent solution could be the parametrized routines.

Parametrized routines

do_something [G] (a: ARRAY [G]): G
	require
		a.count >= 1
	do
		Result := a.item (1)
	end

Right abstraction

The wildcard generics and the parametrized routines introduce new constructs for Eiffel. Is there another solution avoiding this?

With a right abstraction it is possible to have flexible classes. It is comparable to the imuutable cocncept.

example: V_CONTAINER class of Eiffel Base 2 To obtain the genric covariance behavior it is necessary to have:

  • V_ITERATOR must have a covariant generic.
  • TUPLE should be a read-only interface (V_MUTABLE_TUPLE would be the current TUPLE class). Therefore TUPLE could be conformed to V_SEQUENCE and V_MUTABLE_TUPLE conformed to V_ARRAY. Note that only the readonly interface could be in the Eiffel Standard.

In V_CONTAINER class only two features should use the variant typing:

new_cursor: V_ITERATOR [G]
	do ... end
 
occurrences (v: variant G): INTEGER
		do
			-- G -> ANY then 'v' type = ANY
			across Current as it loop
				if it.item = v then
					Result := Result + 1
				end
			end
		end
 
has (v: variant G)
	local
		it: like new_cursor
	do
		-- G -> ANY then 'v' type = ANY
		it := new_cursor -- 'it' type = V_ITERATOR [ANY]
		it.search_forth (v)
		Result := not it.after
	ensure
		occurrences (v) = 1	
	end

In V_ITERATOR only two features should use the variant typing without object test!

With these changes the next code is valid:

local
	a: V_CONTAINER [ANY]; b: V_CONTAINER [STRING]
do
	a := b
 
end

Sub-conclusion

With a right abstraction and a smart use of variant typing for generics, it is possible to obtain a greater flexibility keeping a fully aivailable interface.

Limit changes

To limit code change and to increase typing consistency, we can change the semantic of 'variant'.

Contravariant redefinition of request result is not very useful. The variant typing could be limited for covariant redefinition. There could have no effect on request result.

For generic conformance, a formal generic prefixed with the variant mark is covariant. The variant mark is not required on feature argument limiting code change.

However we have a problem: How mark a contravariant generic? There is three options:

  • Another mark.
  • 'frozen' mark for novariant generic and then no mark for contravariant generic: too restrictive since novariance concerns a lot of generic classes
  • No prefixed mark means novariant or contravariant generic

Export status restrictions

Since the ECMA Eiffel Standard forbids the export restriction with conforming inheritance, it is not a problem. However, the semantic can be changed to enable this restriction on conforming inheritance.

Restrict exportation should not cause 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 more simple and readable.

Conclusion

The proposition uses no new keyword and solves the catcall problem. It passes all Catcall checkpoints.

The new generic conformance gives a natural, safe, and flexible static typing. The variant typing allows to create adaptive interfaces keeping a safe static typing.

With a fine abstraction and the use of variant typing for generics, the genric conformance flexibility is little restricted.

There is no interface restriction (Interval types or Usage-site variance). Class interfaces are fully available.

Your view

name: comment:

...