A practical List.push example / understanding "null" in List usage

The examples from the documentations use “null” in place of lists for operations like:

List.push<Nat>(0, null) // => ?(0, null);

the resulting value doesn’t, at a glance, appear to be a List. This was confusing. I would like to see more practical examples for the List operations.

I’m also wondering, is “null” interpreted as an empty list?
What does “?(0, null);” mean?

I would expect something like:

var numbers = List.nil<Nat>;
numbers := List.append<Nat>(1, numbers); //List<Nat>[1]

The List type is represented as an optional pair of a value and a tail list:

public type List<T> = ?(T, List<T>);

This effectively is a single-linked list, where null (the empty option) represents the empty list.

let a = null;              // [], same as List.nil<T>
let b = ?(1, null);        // [1]
let c = ?(1, ?(2, null));  // [1, 2]
let d = ?(0, c);           // [0, 1, 2], same as List.push(0, c)

List.append on the other hand concatenates two lists, so your example won’t type-check. This does:

let e = List.append(d, d); // [0, 1, 2, 0, 1, 2]