%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 1995-1997, 1999-2001, 2004-2006, 2010-2011 The University of Melbourne.
% Copyright (C) 2013-2023, 2025-2026 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% File: assoc_list.m.
% Main authors: fjh, zs.
% Stability: high.
%
% This file defines the type assoc_list(K, V), which holds a list of
% key-value pairs, and some predicates which operate on assoc_lists.
%
% Another module of the Mercury standard library, kv_list.m, defines
% another data type that does the same job, but makes different tradeoffs.
%
%--------------------------------------------------%
%--------------------------------------------------%
:- module assoc_list.
:- interface.
:- import_module list.
:- import_module pair.
%--------------------------------------------------%
:- type assoc_list(K, V) == list(pair(K, V)).
:- type assoc_list(T) == list(pair(T, T)).
%--------------------------------------------------%
% These instantiation states can be used for instantiation
% state subtyping.
%
:- inst assoc_list(I1, I2) == list(pair(I1, I2)).
:- inst assoc_list(I) == list(pair(I, I)).
%--------------------------------------------------%
%
% Creating assoc_lists from lists of keys and values.
%
% Zip together a list of keys and a list of values.
% Throw an exception if they are of different lengths.
%
:- func from_corresponding_lists(list(K), list(V)) = assoc_list(K, V).
:- pred from_corresponding_lists(list(K)::in, list(V)::in,
assoc_list(K, V)::out) is det.
% Zip together a list of keys and a list of values.
% Fail if they are of different lengths.
%
:- pred maybe_from_corresponding_lists(list(K)::in, list(V)::in,
assoc_list(K, V)::out) is semidet.
%--------------------------------------------------%
%
% Operations on lists of keys and/or values.
%
% Swap the two sides of the pairs in each member of the list.
%
:- func reverse_members(assoc_list(K, V)) = assoc_list(V, K).
:- pred reverse_members(assoc_list(K, V)::in, assoc_list(V, K)::out) is det.
% Return the first member of each pair.
%
:- func keys(assoc_list(K, V)) = list(K).
:- pred keys(assoc_list(K, V)::in, list(K)::out) is det.
% Return the second member of each pair.
%
:- func values(assoc_list(K, V)) = list(V).
:- pred values(assoc_list(K, V)::in, list(V)::out) is det.
% Return two lists containing respectively the first and the second member
% of each pair in the assoc_list.
%
:- pred keys_and_values(assoc_list(K, V)::in,
list(K)::out, list(V)::out) is det.
%--------------------------------------------------%
%
% Searching assoc_lists.
%
% Find the first element of the association list that matches
% the given key, and return the associated value.
% Fail if there is no matching key.
%
:- pred search(assoc_list(K, V)::in, K::in, V::out) is semidet.
% Find the first element of the association list that matches
% the given key, and return the associated value.
% Throw an exception if there is no matching key.
%
:- pred lookup(assoc_list(K, V)::in, K::in, V::out) is det.
% A field access version of search.
%
:- func assoc_list(K, V) ^ elem(K) = V is semidet.
% A field access version of lookup.
%
:- func assoc_list(K, V) ^ det_elem(K) = V is det.
%--------------------------------------------------%
%
% Updating elements in assoc_lists.
%
% Find the first element of the assoc_list list that matches
% the given key, and update the associated value.
% Fail if there is no matching key.
%
:- pred update(K::in, V::in, assoc_list(K, V)::in, assoc_list(K, V)::out)
is semidet.
%--------------------------------------------------%
%
% Removing elements from assoc_lists.
%
% Find the first element of the association list that matches
% the given key. Return the associated value, and the original list
% with the selected element removed.
%
:- pred remove(assoc_list(K, V)::in, K::in, V::out, assoc_list(K, V)::out)
is semidet.
% As above, but with an argument ordering that is more conducive to
% the use of state variable notation.
%
:- pred svremove(K::in, V::out, assoc_list(K, V)::in, assoc_list(K, V)::out)
is semidet.
%--------------------------------------------------%
%
% Mapping keys or values.
%
:- func map_keys_only(func(K) = L, assoc_list(K, V)) = assoc_list(L, V).
:- pred map_keys_only(pred(K, L)::in(pred(in, out) is det),
assoc_list(K, V)::in, assoc_list(L, V)::out) is det.
:- func map_values_only(func(V) = W, assoc_list(K, V)) = assoc_list(K, W).
:- pred map_values_only(pred(V, W)::in(pred(in, out) is det),
assoc_list(K, V)::in, assoc_list(K, W)::out) is det.
:- func map_values(func(K, V) = W, assoc_list(K, V)) = assoc_list(K, W).
:- pred map_values(pred(K, V, W)::in(pred(in, in, out) is det),
assoc_list(K, V)::in, assoc_list(K, W)::out) is det.
%--------------------------------------------------%
%
% Filtering elements in assoc_lists.
%
% filter(Pred, List, TrueList):
%
% Given a closure containing a predicate with one input argument,
% and a list of key-value pairs, call the closure on each key K.
% Include the key-value pair in TrueList if-and-only-if Pred(K) is true.
%
:- func filter(pred(K)::in(pred(in) is semidet),
assoc_list(K, V)::in) = (assoc_list(K, V)::out) is det.
:- pred filter(pred(K)::in(pred(in) is semidet),
assoc_list(K, V)::in, assoc_list(K, V)::out) is det.
% negated_filter(Pred, List, FalseList):
%
% Given a closure containing a predicate with one input argument,
% and a list of key-value pairs, call the closure on each key K.
% Include the key-value pair in FalseList if-and-only-if Pred(K) is false.
%
:- func negated_filter(pred(K)::in(pred(in) is semidet),
assoc_list(K, V)::in) = (assoc_list(K, V)::out) is det.
:- pred negated_filter(pred(K)::in(pred(in) is semidet),
assoc_list(K, V)::in, assoc_list(K, V)::out) is det.
% filter(Pred, List, TrueList, FalseList):
%
% Given a closure containing a predicate with one input argument,
% and a list of key-value pairs, call the closure on each key K.
% Include the key-value pair in TrueList if-and-only-if Pred(K) is true.
% Include the key-value pair in FalseList if-and-only-if Pred(K) is false.
%
:- pred filter(pred(K)::in(pred(in) is semidet),
assoc_list(K, V)::in, assoc_list(K, V)::out, assoc_list(K, V)::out) is det.
%--------------------------------------------------%
%
% Operations on two assoc_lists.
%
% merge(ListA, ListB, ListAB):
%
% Given two lists ListA and ListB, which must both be sorted
% in ascending order on the keys, return ListAB, which is the result
% of merging the elements of ListA and ListB. It will also be sorted
% in ascending order.
%
:- func merge(assoc_list(K, V), assoc_list(K, V)) = assoc_list(K, V).
:- pred merge(assoc_list(K, V)::in, assoc_list(K, V)::in,
assoc_list(K, V)::out) is det.
% common_subset(ListA, ListB, CommonList):
%
% Given two lists ListA and ListB, which must both be sorted
% in ascending order on the keys, neither of which contains any key
% more than once, return CommonList, which will consist of only the
% key-value pairs that occur in *both* ListA and ListB.
% It will also be sorted in ascending order on the keys.
%
:- func common_subset(assoc_list(K, V), assoc_list(K, V)) = assoc_list(K, V).
%--------------------------------------------------%
%
% Folding over assoc_lists.
%
% foldl(Pred, List, Start, End):
%
% Call Pred with each key-value pair in List, working left-to-right.
% Each call updates the value of an accumulator. The initial value
% of this accumulator is passed by the caller as Start; this predicate
% returns its final value as End.
%
:- pred foldl(pred(K, V, A, A), assoc_list(K, V), A, A).
:- mode foldl(in(pred(in, in, in, out) is det), in, in, out) is det.
:- mode foldl(in(pred(in, in, mdi, muo) is det), in, mdi, muo) is det.
:- mode foldl(in(pred(in, in, di, uo) is det), in, di, uo) is det.
:- mode foldl(in(pred(in, in, in, out) is semidet), in, in, out) is semidet.
:- mode foldl(in(pred(in, in, mdi, muo) is semidet), in, mdi, muo) is semidet.
:- mode foldl(in(pred(in, in, di, uo) is semidet), in, di, uo) is semidet.
:- mode foldl(in(pred(in, in, in, out) is nondet), in, in, out) is nondet.
% foldl_keys(Func, List, Start) = End:
% foldl_keys(Pred, List, Start, End):
%
% Call Func or Pred with the keys in List, working left-to-right.
% Each call updates the value of an accumulator. The initial value
% of this accumulator is passed by the caller as Start; this predicate
% returns its final value as End.
%
:- func foldl_keys(func(K, A) = A, assoc_list(K, V), A) = A.
:- pred foldl_keys(pred(K, A, A), assoc_list(K, V), A, A).
:- mode foldl_keys(in(pred(in, in, out) is det), in, in, out) is det.
:- mode foldl_keys(in(pred(in, mdi, muo) is det), in, mdi, muo) is det.
:- mode foldl_keys(in(pred(in, di, uo) is det), in, di, uo) is det.
:- mode foldl_keys(in(pred(in, in, out) is semidet), in, in, out) is semidet.
:- mode foldl_keys(in(pred(in, mdi, muo) is semidet), in, mdi, muo) is semidet.
:- mode foldl_keys(in(pred(in, di, uo) is semidet), in, di, uo) is semidet.
:- mode foldl_keys(in(pred(in, in, out) is multi), in, in, out) is multi.
:- mode foldl_keys(in(pred(in, in, out) is nondet), in, in, out) is nondet.
% foldl_values(Func, List, Start) = End:
% foldl_values(Pred, List, Start, End):
%
% Call Func or Pred with the values in List, working left-to-right.
% Each call updates the value of an accumulator. The initial value
% of this accumulator is passed by the caller as Start; this predicate
% returns its final value as End.
%
:- func foldl_values(func(V, A) = A, assoc_list(K, V), A) = A.
:- pred foldl_values(pred(V, A, A), assoc_list(K, V), A, A).
:- mode foldl_values(in(pred(in, in, out) is det), in,
in, out) is det.
:- mode foldl_values(in(pred(in, mdi, muo) is det), in,
mdi, muo) is det.
:- mode foldl_values(in(pred(in, di, uo) is det), in,
di, uo) is det.
:- mode foldl_values(in(pred(in, in, out) is semidet), in,
in, out) is semidet.
:- mode foldl_values(in(pred(in, mdi, muo) is semidet), in,
mdi, muo) is semidet.
:- mode foldl_values(in(pred(in, di, uo) is semidet), in,
di, uo) is semidet.
:- mode foldl_values(in(pred(in, in, out) is multi), in,
in, out) is multi.
:- mode foldl_values(in(pred(in, in, out) is nondet), in,
in, out) is nondet.
% Does the same job as foldl, but with two accumulators.
%
:- pred foldl2(pred(K, V, A, A, B, B), assoc_list(K, V), A, A, B, B).
:- mode foldl2(in(pred(in, in, in, out, in, out) is det), in, in, out,
in, out) is det.
:- mode foldl2(in(pred(in, in, in, out, mdi, muo) is det), in, in, out,
mdi, muo) is det.
:- mode foldl2(in(pred(in, in, in, out, di, uo) is det), in, in, out,
di, uo) is det.
:- mode foldl2(in(pred(in, in, in, out, in, out) is semidet), in, in, out,
in, out) is semidet.
:- mode foldl2(in(pred(in, in, in, out, mdi, muo) is semidet), in, in, out,
mdi, muo) is semidet.
:- mode foldl2(in(pred(in, in, in, out, di, uo) is semidet), in, in, out,
di, uo) is semidet.
:- mode foldl2(in(pred(in, in, in, out, in, out) is nondet), in, in, out,
in, out) is nondet.
% Does the same job as foldl_values, but with two accumulators.
%
:- pred foldl2_values(pred(V, A, A, B, B), assoc_list(K, V),
A, A, B, B).
:- mode foldl2_values(in(pred(in, in, out, in, out) is det), in,
in, out, in, out) is det.
:- mode foldl2_values(in(pred(in, in, out, mdi, muo) is det), in,
in, out, mdi, muo) is det.
:- mode foldl2_values(in(pred(in, in, out, di, uo) is det), in,
in, out, di, uo) is det.
:- mode foldl2_values(in(pred(in, in, out, in, out) is semidet), in,
in, out, in, out) is semidet.
:- mode foldl2_values(in(pred(in, in, out, mdi, muo) is semidet), in,
in, out, mdi, muo) is semidet.
:- mode foldl2_values(in(pred(in, in, out, di, uo) is semidet), in,
in, out, di, uo) is semidet.
:- mode foldl2_values(in(pred(in, in, out, in, out) is multi), in,
in, out, in, out) is multi.
:- mode foldl2_values(in(pred(in, in, out, in, out) is nondet), in,
in, out, in, out) is nondet.
% Does the same job as foldl, but with three accumulators.
%
:- pred foldl3(pred(K, V, A, A, B, B, C, C), assoc_list(K, V),
A, A, B, B, C, C).
:- mode foldl3(in(pred(in, in, in, out, in, out, in, out) is det), in,
in, out, in, out, in, out) is det.
:- mode foldl3(in(pred(in, in, in, out, in, out, mdi, muo) is det), in,
in, out, in, out, mdi, muo) is det.
:- mode foldl3(in(pred(in, in, in, out, in, out, di, uo) is det), in,
in, out, in, out, di, uo) is det.
:- mode foldl3(in(pred(in, in, in, out, in, out, in, out) is semidet), in,
in, out, in, out, in, out) is semidet.
:- mode foldl3(in(pred(in, in, in, out, in, out, mdi, muo) is semidet), in,
in, out, in, out, mdi, muo) is semidet.
:- mode foldl3(in(pred(in, in, in, out, in, out, di, uo) is semidet), in,
in, out, in, out, di, uo) is semidet.
:- mode foldl3(in(pred(in, in, in, out, in, out, in, out) is nondet), in,
in, out, in, out, in, out) is nondet.
% Does the same job as foldl_values, but with three accumulators.
%
:- pred foldl3_values(pred(V, A, A, B, B, C, C), assoc_list(K, V),
A, A, B, B, C, C).
:- mode foldl3_values(in(pred(in, in, out, in, out, in, out) is det),
in, in, out, in, out, in, out) is det.
:- mode foldl3_values(in(pred(in, in, out, in, out, mdi, muo) is det),
in, in, out, in, out, mdi, muo) is det.
:- mode foldl3_values(in(pred(in, in, out, in, out, di, uo) is det),
in, in, out, in, out, di, uo) is det.
:- mode foldl3_values(in(pred(in, in, out, in, out, in, out) is semidet),
in, in, out, in, out, in, out) is semidet.
:- mode foldl3_values(in(pred(in, in, out, in, out, mdi, muo) is semidet),
in, in, out, in, out, mdi, muo) is semidet.
:- mode foldl3_values(in(pred(in, in, out, in, out, di, uo) is semidet),
in, in, out, in, out, di, uo) is semidet.
:- mode foldl3_values(in(pred(in, in, out, in, out, in, out) is multi),
in, in, out, in, out, in, out) is multi.
:- mode foldl3_values(in(pred(in, in, out, in, out, in, out) is nondet),
in, in, out, in, out, in, out) is nondet.
%--------------------------------------------------%
%--------------------------------------------------%