import Base # Pure chain-reading cores; LAWS.bend quantifies over these, never over IO. # Entries are a named Data constructor because a & pair defaults to kind # Type, which List<&2, _> rejects. type ChainEntry is Data: Entry{entry_key: String, entry_value: String} # Builds the "namespace/record" lookup key. def encode_lookup_key(+namespace_name: String, +record_identifier: String) -> String: namespace_name ++ "/" ++ record_identifier # Some on match, otherwise the recursive fallback. def pick_match(key_matches: Bool, stored_value: String, fallback: Maybe<&2, String>) -> Maybe<&2, String>: match key_matches: case True{}: Some{stored_value} case False{}: fallback # Most-recent-wins: the head of the chain is the newest commit. def lookup_in_chain(+namespace_name: String, +record_identifier: String, ancestor_chain: List<&2, ChainEntry>) -> Maybe<&2, String>: match ancestor_chain: case Nil{}: None{} case Con{head_entry, remaining_chain}: match head_entry: case Entry{entry_key, entry_value}: pick_match(String.eq(entry_key, encode_lookup_key(namespace_name, record_identifier)), entry_value, lookup_in_chain(namespace_name, record_identifier, remaining_chain)) # A tombstone reads as absent; any other value survives. def drop_tombstone(is_tombstone: Bool, stored_value: String) -> Maybe<&2, String>: match is_tombstone: case True{}: None{} case False{}: Some{stored_value} # Maps the TOMBSTONE sentinel to None. def resolve_tombstone(maybe_value: Maybe<&2, String>) -> Maybe<&2, String>: match maybe_value: case None{}: None{} case Some{+stored_value}: drop_tombstone(String.eq(stored_value, "TOMBSTONE"), stored_value) # Drops the entry on match, keeps it otherwise. def drop_or_keep(is_match: Bool, stored_key: String, stored_value: String, filtered_tail: List<&2, ChainEntry>) -> List<&2, ChainEntry>: match is_match: case True{}: filtered_tail case False{}: Con{Entry{stored_key, stored_value}, filtered_tail} # Removes the head entry when its key equals the target. def filter_entry(head_entry: ChainEntry, filtered_tail: List<&2, ChainEntry>, target_key: String) -> List<&2, ChainEntry>: match head_entry: case Entry{+entry_key, +entry_value}: drop_or_keep(String.eq(entry_key, target_key), entry_key, entry_value, filtered_tail) # Removes every entry whose key equals the target. def remove_key_matches(+target_key: String, ancestor_chain: List<&2, ChainEntry>) -> List<&2, ChainEntry>: match ancestor_chain: case Nil{}: Nil{} case Con{head_entry, remaining_chain}: filter_entry(head_entry, remove_key_matches(target_key, remaining_chain), target_key) # Keeps the head and drops its older duplicates from the normalized tail. def attach_head(head_entry: ChainEntry, normalized_tail: List<&2, ChainEntry>) -> List<&2, ChainEntry>: match head_entry: case Entry{+entry_key, +entry_value}: Con{Entry{entry_key, entry_value}, remove_key_matches(entry_key, normalized_tail)} # Drops older duplicates, keeping the first (newest) occurrence of each key. def normalize_chain(ancestor_chain: List<&2, ChainEntry>) -> List<&2, ChainEntry>: match ancestor_chain: case Nil{}: Nil{} case Con{head_entry, remaining_chain}: attach_head(head_entry, normalize_chain(remaining_chain)) # Shared vocabulary: other modules build ChainEntry through this constructor. def make_entry(entry_key: String, entry_value: String) -> ChainEntry: Entry{entry_key, entry_value} # Projects the key out of an entry. def entry_key_of(chain_entry: ChainEntry) -> String: match chain_entry: case Entry{entry_key, entry_value}: entry_key # Projects the value out of an entry. def entry_value_of(chain_entry: ChainEntry) -> String: match chain_entry: case Entry{entry_key, entry_value}: entry_value