# Difference between revisions of "Usage-site variance"

## Introduction

The usage-site variance allows the programmer to choose which kind of variance he wants to use when a generic derivation is declared. This allows to use every generic as novariant, covariant or contravariant in contrast to the definition-site variance where it is set in the class definition.

## Syntax

The syntax used here to specify the variance of a generic is simple and may be changed to something more verbose but also clearer:

• To specify a novariant generic, the normal syntax can be used: `LIST [ANY]`
• To specify a covariant generic, we use a plus sign: `LIST [+ANY]`
• To specify a contravariant generic, we use a minus sign: `LIST [-ANY]`

## Semantics

See the introduction to examples for information about the classes used.

### Conformance rules

Depending on the variance of the generic, the conformance rules differ:

• A generic conforms to a novariant generic if it has the exact same generic parameter. Thus `LIST [T]` only conforms to `LIST [T]`. Note that `LINKED_LIST [T]` also conforms to `LIST [T]` as long as the generic parameter matches.
• A generic conforms to a covariant generic if its generic parameter conforms to the generic parameter of the covariant generic. Thus `LIST [U]` conforms to `LIST [+T]`.
• A generic conforms to a contravariant generic if its generic parameter is a parent of the generic parameter of the contravariant generic. Thus `LIST [T]` conforms to `LIST [-U]`.

For the conformance of tuples, we keep that a tuple with more elements conforms to a tuple with less elements as long as the common elements conform:

• `TUPLE [T, U]` conforms to `TUPLE [T]`
• `TUPLE [U, U]` conforms to `TUPLE [+T]`
• `TUPLE [T, U]` conforms to `TUPLE [-U]`

### Applicable features

Depending on the variance of the generic, the applicable features differ:

• On a novariant generic, all features can be used.
• On a covariant generic, only features which have the formal generic as a result type can be used. If the formal generic appears in an argument this feature is invalid.
• On a contravariant generic, only feature which have the formal generc as an argument type can be used. If the formal generic appears as a result type this feature is invalid.

## Examples

See the introduction to examples for information about the classes used.

### A simple generic algorithm

It is easy to declare an algorithm over generics as long as only read-access is needed. Just declare input to the algorithm as covariant list.

```class PERSON_PRINTER
feature
print_all (a_list: LIST [+PERSON])
-- Print all attributes of each person in the list.
do
from
a_list.start
until
a_list.after
do
-- Reading is valid on a covariant list
a_list.item.print
end
end
end

class EXAMPLE
feature
make
local
l_students: LIST [STUDENT]
l_professors: LIST [PROFESSOR]
l_person_printer: PERSON_PRINTER
do
create l_person_printer
-- LIST [STUDENT] conforms to LIST [+PERSON]
l_person_printer.print_all (l_students)
-- LIST [PROFESSPR] conforms to LIST [+PERSON]
l_person_printer.print_all (l_professor)
end
end```

### Comparator

In the example of sorting a list with a comparator, contravariant generics can be used. Due to the use of `COMPARATOR [-G]` when sorting a `LIST [G]`, we allow to use a comparator which can sort more generally than just objects of type `G`. This allows to use a comparator for `ANY` objects to sort a list of strings.

```class SORTER [G]
feature
sort (a_list: LIST [G]; a_comparator: COMPARATOR [-G])
do
-- Somewhere in the loop:
a_comparator.compare (l_string_1, l_string_2)
end
end

class EXAMPLE
feature
make
local
l_list: LIST [STRING]
l_string_sorter: SORTER [STRING]
l_string_comparator: COMPARATOR [STRING]
l_any_comparator: COMPARATOR [ANY]
do
-- COMPARATOR [STRING] conforms to COMPARATOR [-STRING]
l_string_sorter.sort (l_list, l_string_comparator)
-- COMPARATOR [ANY] conforms to COMPARATOR [-STRING]
l_string_sorter.sort (l_list, l_any_comparator)
end
end```

### Agents

For agents, we will look at an example with the type `T` and a subtype `U`. We will see an agent declaration and then check which arguments that are allowed:

#### Calling agents

```local
an_agent: PROCEDURE [+ANY, -TUPLE [+T]]
-- An agent which takes an argument of type T.
do
-- The following calls are surely permitted (and also correct) since
-- the tuple generic inside the tuple is covariant and thus allows
-- the tuple of type U to be passed.
an_agent.call ([T])
an_agent.call ([U])

-- Due to the tuple conformance, the following calls are also
-- permitted. Note that they are both correct.
an_agent.call ([T, ...])
an_agent.call ([U, ...])
end```

We see that this solution allows the full range of applicable arguments for calling agents.

#### Assigning agents

```local
an_agent: PROCEDURE [+ANY, -TUPLE [+T]]
-- An agent which takes an argument of type T.
do
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]]
agent_tt := agent (t: T; t2: T) do end --> PROCEDURE [ANY, TUPLE [+T, +T]]

-- This assignment is naturally allowed.
an_agent := agent_t

-- This assignment is allowed since the tuple is declared contravariant
-- and the empty tuple is an ancestor of TUPLE [+T]
an_agent := agent_empty

-- This assignment is allowed and correct. The reason is that TUPLE [+ANY]
-- is an ancestor of TUPLE [+T].
an_agent := agent_any

-- The following assignments are not permitted by the solution. This is the correct
-- behaviour  as you could either create a catcall by passing a T argument to the
-- `agent_u' or pass the wrong number of arguments by passing a [T] tuple to the
-- `agent_tt'.
an_agent := agent_u
an_agent := agent_tt
end```

We see that this solution allows all possible agents to be assigned.

Since declaring an agent can be difficult (where did I need to put the "+" and the "-"?), an alternate syntax for agents could be useful where the compiler can derive the correct type for an agent.

#### Vision 2 Example

A field which makes heavy use of agents and benefits a lot of them is event driven programming. With the current agent mechanism the programmer is sometimes forced to use a hack to circumvent the type checker in order to gain flexibility.

The following code is taken from `EV_PND_MOTION_ACTION_SEQUENCE`. Here we gain the flexibility that an agent which listens to the event does not have to be a feature which takes all the data provided if the event occurs.

```force_extend (action: PROCEDURE [ANY, TUPLE]) is
-- Extend without type checking.
do
extend (agent wrapper (?, ?, ?, action))
end

wrapper (an_x, a_y: INTEGER; a_pick_and_dropable: EV_ABSTRACT_PICK_AND_DROPABLE; action: PROCEDURE [ANY, TUPLE]) is
-- Use this to circumvent tuple type checking. (at your own risk!)
-- Calls `action' passing all other arguments.
do
action.call ([an_x, a_y, a_pick_and_dropable])
end```

If this solution is built into Eiffel the designer of Vision2 would do a small change in `ACTION_SEQUENCE`. The changes makes sure that we can accept agents to features defined in ancestors of `ANY`, therefore the `+ANY` and that we accept agents which are not interested in all data but just (possibly) take parts of it.

```class
ACTION_SEQUENCE [EVENT_DATA -> +TUPLE create default_create end]

inherit
-- old ARRAYED_LIST [PROCEDURE [ANY, EVENT_DATA]]
ARRAYED_LIST [PROCEDURE [+ANY, -EVENT_DATA]]
rename
make as arrayed_list_make
export
{ACTION_SEQUENCE} same_items, subarray
redefine
default_create,
set_count
end```

The wrapper code can be removed from `EV_PND_MOTION_ACTION_SEQUENCE` and we also change one line. This change empowers the system to accept descendants of `EV_ABSTRACT_PICK_AND_DROPABLE` as event data. The two integers do not need be marked covariantly as they are frozen and no descendants exist for them.

```class
EV_PND_MOTION_ACTION_SEQUENCE

inherit
-- old EV_ACTION_SEQUENCE [TUPLE [x: INTEGER; y: INTEGER; pick_and_dropable: EV_ABSTRACT_PICK_AND_DROPABLE]]
EV_ACTION_SEQUENCE [TUPLE [x: INTEGER; y: INTEGER; pick_and_dropable: +EV_ABSTRACT_PICK_AND_DROPABLE]]
...
end```

Now with this the actual signature of `{EV_PND_MOTION_ACTION_SEQUENCE}.extend` looks like this:

`extend (v: PROCEDURE [ANY, -TUPLE [x: INTEGER_32; y: INTEGER_32; +pick_and_dropable: EV_ABSTRACT_PICK_AND_DROPABLE]]`

For the sake of completeness the signature of `{EV_PND_MOTION_ACTION_SEQUENCE}.call`. Note again that the the field `pick_and_dropable' is covariant allowing to call the event with tuples containing descendants of `EV_ABSTRACT_PICK_AND_DROPABLE`.

`call (event_data: TUPLE [x: INTEGER_32; y: INTEGER_32; pick_and_dropable: +EV_ABSTRACT_PICK_AND_DROPABLE])`

## Conclusion

The usage-site variance is a mechanism which allows both expressive and type-safe generics. It can model agents and comparator types correctly.