Preventing CAT calls in Generics
Warning: Warning: Article under development
Contents
Introduction
Example
class T end class U inherit T -- no covariant feature redefinition end
Conformance
Type conformance
conforms to | T | T..NONE | ANY..T | U |
---|---|---|---|---|
T | true | false | true | false |
T..NONE | true | false | true | false |
ANY..T | false | false | false | false |
U | true | false | true | true |
Generic conformance
conforms to | LIST [ANY] | LIST [T] | LIST [T..NONE] | LIST [ANY..T] | LIST [U] |
---|---|---|---|---|---|
LIST [ANY] | true | false | false | true | false |
LIST [T] | false | true | true | true | false |
LIST [T..NONE] | false | false | true | false | false |
LIST [ANY..T] | false | false | false | true | false |
LIST [U] | false | false | true | false | true |
-- legal T := LIST [T..NONE] .item LIST [T..NONE] .put (Void) -- illegal U := LIST [T..NONE] .item LIST [T..NONE] .put (T) -- legal LIST [ANY..T] .put (T) LIST [ANY..T] .put (U) ANY := LIST [ANY..T] .item -- illegal LIST [ANY..T] .put (ANY) T := LIST [ANY..T] .item
How should one understand this?
class PERSON end class STUDENT inherit PERSON end class EMPLOYEE inherit PERSON end class EMPLOYED_STUDENT inherit EMPLOYEE STUDENT end
What this actually is, is an extension to the type system. It helps you to proper derive an interface of a generic type.
For example an actual type parameter for G in LIST [G]
is [PERSON..EMPLOYED_STUDENT].
The interface generated by this derivations is simply that the first type (PERSON) is taken wherever G occurs as a return type.
The second type EMPLOYED_STUDENT is taken wherever G occurs as an argument type.
The conformance rules for such new interfaces simply reflect that arguments are contra-variant and return types are covariant.
(generic derived) class LIST[PERSON..EMPLOYED_STUDENT] put (v: EMPLOYED_STUDENT) item: PERSON
That's basically all there is to say about this enhancement to the Eiffel type system. Except that one might adapt the syntax, another idea is: <type_for_arguments>:<type_for_return_types> This would more resemble an actual generated signature of a feature which looks initialy like
class EXAMPLE [G] feature example (a_g: G): G end
A type declaration with this syntax would look like:
local l_example: EXAMPLE [EMLPOYED_STYDENT: STUDENT] l_employed_student: EMPLOYED_STUDENT l_person: PERSON do -- l_example.example (a_g: EMPLOYED_STUDENT): STUDENT l_person := l_example.example (l_employed_student) end
But let's stick with the initial syntax for now.
Agents
The neat thing about this extension is, that there is no need to cut down the expressiveness of the agent mechanism to make them perfectly save to use with full support for all legal situations.
Procedure class:
class PROCEDURE [BASE_TYPE, OPEN_ARGS -> TUPLE []] -- Signature of call is generated by the compiler. To visualize this we add _reflected to the argument tuple. call (args: OPEN_ARGS_reflected) -- valid do end end
agent (T)
Type declaration:
an_agent: PROCEDURE [ANY..NONE, TUPLE..TUPLE [ANY..T]] --> like agent (T) -- signature for call has to be inverted: TUPLE [T..NONE]..NONE -- legal an_agent.call ([T]) an_agent.call ([T, ...]) an_agent.call ([U, ...]) -- illegal an_agent.call ([])
Instantiation:
agent_empty := agent () do end --> PROCEDURE [ANY, TUPLE []] agent_any := agent (a: ANY) do end --> PROCEDURE [ANY, TUPLE [ANY]] agent_t := agent (t: T) do end --> PROCEDURE [ANY, TUPLE [T]] agent_u := agent (u: U) do end --> PROCEDURE [ANY, TUPLE [U]] -- legal an_agent := agent_empty an_agent := agent_any an_agent := agent_t -- illegal an_agent := agent_u
There might be a concern about the fact that the compiler generates the signature of the call method. But I think that its not wrong to have compiler support for agents, as they are such a fundamental core concept that is simply important that one could sacrifice expressive power or type safety just to avoid explicit support from the compiler.
How to generate the call signature for a given agent type
<todo>