# Usage-site variance

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

## 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.

## Conclusion

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