liveBook · Manning

Out position

As explained in Fabulous Adventures in Data Structures and Algorithms

An output position is a type-parameter occurrence where values of the parameterized type are produced or returned rather than consumed.

A type parameter is in an output position when a value of that type is produced, returned, or exposed for reading. In C#, a generic interface may mark its parameter covariant with `out` only when every use has this shape. An `IEnumerable<T>` can therefore expose `T` while remaining safely covariant: an `IEnumerable<Giraffe>` can be assigned to an `IEnumerable<Animal>`. section 2.3 section 7.2

A read-only interface

An interface whose type parameter appears only in a getter can be declared covariant: `interface IE<out T> { T Current { get; } bool MoveNext(); }` The assignment `IE<Animal> animals = giraffes;` is then valid when `giraffes` has type `IE<Giraffe>`. The reverse direction is not implied: the interface produces animals, but it does not promise that every produced value is a giraffe. A method such as `Add(T value)` would put `T` in an input position and make covariance unsafe. section 7.2 section 2.3

This restriction explains a surprising immutable-stack design. `Peek()` returns `T`, but `Push(T)` consumes one. Moving `Push` into an extension method leaves the covariant interface with only output uses while preserving the call-site style. A stack of tigers can then be viewed as a stack of animals and extended with a giraffe, producing a new immutable stack rather than mutating the old one. section 2.3

Listing 2.5 A covariant immutable stack
interface IImStack<out T> : IEnumerable<T> #1
{
    T Peek(); #2
    IImStack <T> Pop();
    bool IsEmpty {  get; }
}
class ImStack<T> : IImStack<T>
{
    …
    public static IImStack<T> Push(T item, IImStack<T> tail) => #3
        new ImStack<T>(item, tail);
    …
}
static class Extensions #4
{
    public static IImStack<T> Push<T>(this IImStack<T> stack, T item) => 
        ImStack<T>.Push(item, stack);
}

IImStack<Tiger> s1 = ImStack<Tiger>.Empty;
IImStack<Tiger> s2 = s1.Push(new Tiger());
IImStack<Tiger> s3 = s2.Push(new Tiger());
IImStack<Animal> s4 = s3;  #5
IImStack<Animal> s5 = s4.Push(new Giraffe());
Console.WriteLine(s5.Bracket());

Inspect the complete covariant immutable-stack implementation, including output-only interface members, the static Push operation, its extension-method form, and the Tiger-to-Animal assignment. section 2.3

How Fabulous Adventures in Data Structures and Algorithms covers it

How deep it goes
Explains returned values, getters, consumed parameters, and an extension-method workaround, then connects them to type safety.
Examples
Uses IEnumerable<T>, IE<T>, an immutable stack, and Animal, Giraffe, and Tiger assignments.
What it assumes
Assumes generic interfaces and assignment compatibility while defining input and output positions through concrete C# examples.
What you can do after
Apply it via C# generic interfaces to recognize safe covariance and relocate consuming operations into extension methods.
How it connects
Connects output positions to covariance, generic interfaces, immutability, extension methods, and assignment compatibility.

Also explained in

Compare how 2 books explain out position →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fabulous Adventures in Data Structures and Algorithms. The section links point to the parts of the book it draws on.