# Definition-site variance

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

## Introduction

The defintion-site variance allows the programmer to choose which kind of variance he wants to use in the defintion of a generic class. Unlike the usage-site variance, this fixes a specific class to the kind of variance which it supports.

## 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: class LIST [G] end
• To specify a covariant generic, we use a plus sign: class READ_LIST [+G] end
• To specify a contravariant generic, we use a minus sign: class WRITE_LIST [-G] end

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

• If a generic class is declared novariant, two generics conform if their base class conforms and the generic parameters match exactly. Thus LIST [T] only conforms to LIST [T].
• If a generic class is declared covariant, a generic conforms to another if its base class conforms and the generic parameter conforms to the other. Thus READ_LIST [U] conforms to READ_LIST [T].
• If a generic class is declared contravariant, a generic conforms to another if its base class conforms and the generic parameter is an ancestor of the other. Thus WRITE_LIST [U] conforms to WRITE_LIST [T].

Tuples are problematic in this solution as have to declare the generic tuple parameters as invariant. For the conformance of tuples, we keep that a tuple with more elements conforms to a tuple with less elements. The common elements have to match exactly due to their invariance:

### Applicable features

Depending on the variance of a generic, it can only occur in certain places:

• A novariant generic can be used as argument or result type.
• A covariant generic can only be used as a result type.
• A contravariant generic can only be used as an argument type.

## Examples

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

### A simple generic algorithm

In order to use lists as read-only lists, the library design has to be adapted. LIST [G] needs to inherit from READ_LIST [+G]. That way you can assign a LIST [STRING] to a READ_LIST [STRING] and, through transitivity of conformance, to a READ_LIST [ANY].

class PERSON_PRINTER
feature
-- 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 READ_LIST [PERSON]
l_person_printer.print_all (l_students)
-- LIST [PROFESSOR] conforms to READ_LIST [PERSON]
l_person_printer.print_all (l_professor)
end
end

### Comparator

For the comparator example to work, the comparator class has to be declared as contravariant generic: class COMPARATOR [-G].

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

Again, to use definition-site variance you have to adapt the library design. The procedure class has to be defined as follows:

class PROCEDURE [+BASE_TYPE, -OPEN_ARGS -> TUPLE]
end

#### 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 as the generic
-- parameter in the tuple is novariant since it is not specified
-- further in the procedure class.
an_agent.call ([T])
an_agent.call ([T, ...])

-- Because the tuple is novariant, the following calls are
-- illegal although they would be safe.
an_agent.call ([U])
an_agent.call ([U, ...])
end

In contrast to usage-site variance you cannot express that the generic parameters of the tuple are covariant. Because of that, the call with a subtype of the actual parameter is not permitted by the type system.

#### 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 not allowed although it would be correct. The reason
-- is that TUPLE [ANY] is not an ancestor of TUPEL [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

Again, the fact that the generic tuple parameters are novariant causes that a safe assignment is prohibited.

## Conclusion

This solution requires that the libraries are designed in a way which separates reading and writing to generic datastructes. It allows full use of comparator objects but does not fully support agents.

On the plus side, the client of a library does not need to put variance markers as in the usage-site variance proposal which makes the use of the solution easier for the normal programmer.