Difference between revisions of "Default and explicit variance"
m (English corrections) |
m (Discard the extaended variant typing) |
||
(42 intermediate revisions by 2 users not shown) | |||
Line 2: | Line 2: | ||
[[Category:Catcall]] | [[Category:Catcall]] | ||
{{Research}} | {{Research}} | ||
− | + | == 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? | 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 === | ||
− | + | * Covariant redefinition of request result is allowed (as currently). | |
− | * Covariant redefinition of feature argument requires a | + | * 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 | + | '''example 1:''' covariant redefinition of feature argument with 'variant' typing on redefined argument |
− | Note | + | Note: |
− | In the interface | + | * 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 115: | Line 38: | ||
last: FOOD | last: FOOD | ||
− | + | feature -- Eating | |
eat (f: like last) | eat (f: like last) | ||
require | require | ||
Line 126: | Line 49: | ||
end | end | ||
</e> | </e> | ||
− | |||
<e> | <e> | ||
class | class | ||
Line 138: | Line 60: | ||
last: GRASS | last: GRASS | ||
− | + | feature -- Eating | |
− | eat (f: variant like last) -- or eat (f: variant | + | eat (f: variant like last) -- or eat (f: variant GRASS) |
require else | require else | ||
True | True | ||
− | -- 'f' type = type | + | -- 'f' type = expected type. Here: GRASS |
do | do | ||
− | -- 'f' type = type of the first | + | -- 'f' type = type of the first definition. Here: FOOD |
− | if attached f as | + | if attached f as expected then |
− | -- ' | + | -- 'expected' type = expected type. Here: GRASS |
− | last := | + | last := expected |
end | end | ||
ensure then | ensure then | ||
True | True | ||
− | -- 'f' type = type of the first | + | -- 'f' type = type of the first definition. Here: FOOD |
end | end | ||
Line 165: | Line 87: | ||
last: FOOD | last: FOOD | ||
− | + | feature -- Eating | |
eat (f: variant like last) | eat (f: variant like last) | ||
do | do | ||
− | if attached f as | + | if attached f as expected then |
− | last := | + | last := expected |
end | end | ||
end | end | ||
Line 189: | Line 111: | ||
</e> | </e> | ||
− | + | === 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:''' | ||
<e> | <e> | ||
− | class | + | deferred class |
− | + | EXAMPLE [variant K, variant G] | |
+ | -- K is contravariant and G is covariant | ||
feature -- Access | feature -- Access | ||
− | + | first: G | |
− | + | item (i: K): G | |
− | + | deferred | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
end | end | ||
− | + | ||
end | end | ||
</e> | </e> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Therefore the next code is valid: | |
− | + | <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> | |
− | + | class | |
− | + | ROUTINE [BASE_TYPE, variant OPEN_ARGS -> detachable TUPLE create default_create end] | |
− | + | -- ... | |
− | + | ||
end | end | ||
</e> | </e> | ||
− | |||
− | + | The generic 'OPEN_ARGS' is used on feature arguments, but also on request result. | |
− | The | + | |
− | + | 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: | |
− | + | ||
− | + | ||
− | + | ||
<e> | <e> | ||
− | operands: | + | operands: detachable OPEN_ARGS |
− | empty_operands: | + | empty_operands: OPEN_ARGS |
do create Result ensure ... end | do create Result ensure ... end | ||
</e> | </e> | ||
− | 'apply' and 'target' | + | 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. | ||
<e> | <e> | ||
apply | apply | ||
+ | require | ||
+ | no_operands: open_count = 0 | ||
do | do | ||
− | + | call (Void) | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
end | end | ||
</e> | </e> | ||
+ | |||
+ | 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 === | === Sub-conclusion === | ||
− | + | The limited variant typing is reasonable and expressive. It limits Eiffel changes and solves all catcall problems. | |
− | + | ||
− | The | + | 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> | |
− | + | 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> | ||
− | A | + | 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. | ||
− | |||
<e> | <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 | end | ||
</e> | </e> | ||
− | The | + | 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> | ||
+ | do_something [G] (a: ARRAY [G]): G | ||
+ | require | ||
+ | a.count >= 1 | ||
+ | do | ||
+ | Result := a.item (1) | ||
+ | end | ||
+ | </e> | ||
+ | |||
+ | ==== 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: | ||
+ | <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 | ||
+ | |||
+ | 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 | ||
+ | </e> | ||
+ | |||
+ | In V_ITERATOR only two features should use the variant typing without object test! | ||
+ | |||
+ | 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 | ||
=== Export status restrictions === | === Export status restrictions === | ||
Line 304: | Line 323: | ||
However, the semantic can be changed to enable this restriction on conforming inheritance. | However, the semantic can be changed to enable this restriction on conforming inheritance. | ||
− | Restrict exportation should not cause a | + | 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 338: | Line 357: | ||
</e> | </e> | ||
− | The class interface is | + | The class interface is more simple and readable. |
== Conclusion == | == Conclusion == | ||
− | The proposition uses no new keyword and | + | The proposition uses no new keyword and solves the catcall problem. |
It passes all [[Catcall checkpoints]]. | It passes all [[Catcall checkpoints]]. | ||
− | The | + | The new generic conformance gives a natural, safe, and flexible static typing. |
− | The variant 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 08: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:
...