Difference between revisions of "Preventing CAT calls in Generics"

(more explanations)
Line 145: Line 145:
 
The second type EMPLOYED_STUDENT is taken wherever G occurs as an argument 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.''
+
  '''The conformance rules for such new interfaces simply reflect that arguments are contra-variant and return types are covariant.'''
  
 
<e>
 
<e>

Revision as of 11:02, 7 April 2007

Warning.png Warning: Warning: Article under development

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>