function createFileMap(keyMapper) {
        var files = {};
        return {
            get: get,
            set: set,
            contains: contains,
            remove: remove,
            forEachValue: forEachValueInMap,
            clear: clear
        };
        function forEachValueInMap(f) {
            for (var key in files) {
                f(key, files[key]);
            }
        }
        // path should already be well-formed so it does not need to be normalized
        function get(path) {
            return files[toKey(path)];
        }
        function set(path, value) {
            files[toKey(path)] = value;
        }
        function contains(path) {
            return hasProperty(files, toKey(path));
        }
        function remove(path) {
            var key = toKey(path);
            delete files[key];
        }
        function clear() {
            files = {};
        }
        function toKey(path) {
            return keyMapper ? keyMapper(path) : path;
        }
    }
Ternary values are defined such that
x & y is False if either x or y is False.
x & y is Maybe if either x or y is Maybe, but neither x or y is False.
x & y is True if both x and y are True.
x | y is False if both x and y are False.
x | y is Maybe if either x or y is Maybe, but neither x or y is True.
x | y is True if either x or y is True.