# 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
across a_list as it
loop
-- Reading is valid on a covariant list
it.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. Maybe the programmer is just interested in the fact that an actual event occurred and not in the data about it.

```force_extend (action: PROCEDURE [ANY, TUPLE]) is
-- Extend without type checking. Not type save! Use on your own risk!
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])
-- 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 descendants of `ANY` (therefore the `+ANY`) and that we accept agents which are not interested in all data but just (possibly) take parts of it (therefore `-EVENT_DATA`).

```class
ACTION_SEQUENCE [EVENT_DATA -> TUPLE create default_create end]
inherit
ARRAYED_LIST [PROCEDURE [+ANY, -EVENT_DATA]]
-- old ARRAYED_LIST [PROCEDURE [ANY, EVENT_DATA]]```

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]]```

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.