doc "Abstract supertype of objects which associate values 
     with keys. `Correspondence` does not satisfy `Category`,
     since in some cases—`List`, for example—it is 
     convenient to consider the subtype a `Category` of its
     values, and in other cases—`Map`, for example—it 
     is convenient to treat the subtype as a `Category` of its
     entries.
     
     The item corresponding to a given key may be obtained 
     from a `Correspondence` using the item operator:
     
         value bg = settings[\"backgroundColor\"] else white;
     
     The `item()` operation and item operator result in an
     optional type, to reflect the possibility that there is
     no item for the given key."
see (Map, List, Category)
by "Gavin"
shared interface Correspondence<in Key, out Item>
        given Key satisfies Object {
    
    doc "Returns the value defined for the given key, or 
         `null` if there is no value defined for the given 
         key."
    see (items)
    shared formal Item? item(Key key);
    
    doc "Determines if there is a value defined for the 
         given key."
    see (definesAny, definesEvery, keys)
    shared default Boolean defines(Key key) {
        return exists item(key);
    }
    
    doc "The `Category` of all keys for which a value is 
         defined by this `Correspondence`."
    see (defines)
    shared default Category keys {
        object keys satisfies Category {
            shared actual Boolean contains(Object key) {
                if (is Key key) {
                    return defines(key);
                }
                else {
                    return false;
                }
            }
        }
        return keys;
    }
    
    doc "Determines if this `Correspondence` defines a value
         for every one of the given keys."
    see (defines)
    shared default Boolean definesEvery(Key... keys) {
        for (key in keys) {
            if (!defines(key)) {
                return false;
            }
        }
        else {
            return true;
        }
    }
    
    doc "Determines if this `Correspondence` defines a value
         for any one of the given keys."
    see (defines)
    shared default Boolean definesAny(Key... keys) {
        for (key in keys) {
            if (defines(key)) {
                return true;
            }
        }
        else {
            return false;
        }
    }
    
    doc "Returns the items defined for the given keys, in
         the same order as the corresponding keys."
    see (item)
    shared default Item?[] items(Key... keys) {
        if (nonempty some = keys.sequence) {
            return Items(some.clone);
        }
        else {
            return {};
        }
    }

    class Items(Sequence<Key> keys)
            extends Object()
            satisfies Sequence<Item?> {
        shared actual Integer lastIndex {
            return keys.lastIndex;
        }
        shared actual Item? first {
            return outer.item(keys.first);
        }
        shared actual Item?[] rest {
            return outer.items(keys.rest...);
        }
        shared actual Item? item(Integer index) {
            if (exists Key key = keys.item(index)) {
                return outer.item(key);
            }
            else {
                return null;
            }
        }
        shared actual Item?[] segment(Integer from, 
                                     Integer length) {
            if (nonempty keys = keys.segment(from,length)) {
                return outer.Items(keys);
            }
            else {
                return {};
            }
        }
        shared actual Item?[] span(Integer from, Integer? to) {
            if (nonempty k = keys.span(from,to)) {
                return outer.Items(keys);
            }
            else {
                return {};
            }
        }
        //shared actual String string { return "Entries"; }
        shared actual Sequence<Item?> clone {
            return this;
        }
        shared actual Sequence<Item?> reversed {
            return outer.Items(keys.reversed);
        }
        shared actual Integer hash {
            return keys.hash;
        }
    }
    
}