    "An implementation of `List` that wraps an `Iterable` of
     elements. All operations on this `List` are performed on
     the `Iterable`."
    by ("Enrique Zamudio")
    shared class LazyList<out Element>({Element*} elems)
            satisfies List<Element> {

        shared actual Integer? lastIndex {
            value size = elems.size;
            return size > 0 then size-1 else null;
        }

        shared actual Element? get(Integer index) {
            if (index == 0) {
                return elems.first;
            }
            else {
                return elems.skipping(index).first;
            }
        }

        shared actual List<Element> rest => LazyList(elems.rest);

        shared actual Iterator<Element> iterator() =>
                elems.iterator();

        "Returns a `List` with the elements of this `List`
         in reverse order. This operation will create copy
         the elements to a new `List`, so changes to the
         original `Iterable` will no longer be reflected in
         the new `List`."
        shared actual List<Element> reversed =>
                elems.sequence.reversed;

        shared actual List<Element> clone => this;

        shared actual List<Element> span
                (Integer from, Integer to) {
            if (to < 0 && from < 0) {
                return [];
            }
            Integer toIndex = largest(to,0);
            Integer fromIndex = largest(from,0);
            if (toIndex >= fromIndex) {
                value els = fromIndex > 0
                        then elems.skipping(fromIndex)
                        else elems;
                return LazyList(els.taking(toIndex-fromIndex+1));
            }
            else {
                //reversed
                value seq = toIndex>0
                        then elems.skipping(toIndex)
                        else elems;
                return seq.taking(fromIndex-toIndex+1)
                        .sequence.reversed;
            }
        }

        shared actual List<Element> spanTo(Integer to) =>
                to<0 then {} else LazyList(elems.taking(to+1));

        shared actual List<Element> spanFrom(Integer from) =>
                from > 0 then LazyList(elems.skipping(from))
                        else this;

        shared actual List<Element> segment
                (Integer from, Integer length) {
            if (length > 0) {
                value els = from > 0 then elems.skipping(from)
                        else elems;
                return LazyList(els.taking(length));
            }
            else {
                return [];
            }
        }

        shared actual default Boolean equals(Object that) {
            if (is List<Anything> that) {
                value size = elems.size;
                if (that.size==size) {
                    for (i in 0..size-1) {
                        value x = this[i];
                        value y = that[i];
                        if (exists x) {
                            if (exists y) {
                                if (x!=y) {
                                    return false;
                                }
                            }
                            else {
                                return false;
                            }
                        }
                        else if (exists y) {
                            return false;
                        }
                    }
                    else {
                        return true;
                    }
                }
            }
            return false;
        }

        shared actual default Integer hash {
            variable value hash = 1;
            for (elem in elems) {
                hash *= 31;
                if (exists elem) {
                    hash += elem.hash;
                }
            }
            return hash;
        }

        shared default actual Element? findLast(
                Boolean selecting(Element elem)) =>
                        elems.findLast(selecting);

        shared actual default Element? first => elems.first;

        shared actual default Element? last => elems.last;

    }