Add get with default to dictionary interface.
[hope.git] / src / hope_kv_list.erl
CommitLineData
03ac148f
SK
1%%%----------------------------------------------------------------------------
2%%% Equivalent to stdlib's orddict, but with a pretty (IMO), uniform interface.
3%%%----------------------------------------------------------------------------
4-module(hope_kv_list).
5
465f1bde 6-behavior(hope_gen_dictionary).
03ac148f
SK
7
8-export_type(
9 [ t/2
10 ]).
11
12-export(
13 [ empty/0
14 , get/2
870172d6 15 , get/3
03ac148f
SK
16 , set/3
17 , update/3
70cf8e86 18 , pop/2
03ac148f
SK
19 , iter/2
20 , map/2
21 , filter/2
22 , fold/3
23 , of_kv_list/1
24 , to_kv_list/1
fa24061d
SK
25 , validate_unique_presence/2 % Assume default optional parameter(s)
26 , validate_unique_presence/3 % Specify optional parameter(s)
03ac148f
SK
27 ]).
28
29
30-type t(K, V) ::
31 [{K, V}].
32
fa24061d
SK
33-type presence_error(A) ::
34 {keys_missing , [A]}
35 | {keys_duplicated , [A]}
36 | {keys_unsupported , [A]}
37 .
38
03ac148f
SK
39
40%% ============================================================================
41%% API
42%% ============================================================================
43
44-spec empty() ->
45 [].
46empty() ->
47 [].
48
95c3c2a7
PO
49-spec get(t(K, V), K) ->
50 hope_option:t(V).
03ac148f
SK
51get(T, K) ->
52 case lists:keyfind(K, 1, T)
53 of false -> none
54 ; {K, V} -> {some, V}
55 end.
56
870172d6
SK
57-spec get(t(K, V), K, V) ->
58 V.
59get(T, K, Default) ->
60 Vopt = get(T, K),
61 hope_option:get(Vopt, Default).
62
95c3c2a7
PO
63-spec set(t(K, V), K, V) ->
64 t(K, V).
03ac148f
SK
65set(T, K, V) ->
66 lists:keystore(K, 1, T, {K, V}).
67
95c3c2a7
PO
68-spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
69 t(K, V).
03ac148f
SK
70update(T, K, F) ->
71 V1Opt = get(T, K),
72 V2 = F(V1Opt),
37c6e98b 73 % TODO: Eliminate the 2nd lookup.
03ac148f
SK
74 set(T, K, V2).
75
95c3c2a7
PO
76-spec pop(t(K, V), K) ->
77 {hope_option:t(V), t(K, V)}.
70cf8e86
SK
78pop(T1, K) ->
79 case lists:keytake(K, 1, T1)
80 of {value, {K, V}, T2} -> {{some, V}, T2}
81 ; false -> {none , T1}
82 end.
83
95c3c2a7
PO
84-spec iter(t(K, V), fun((K, V) -> ok)) ->
85 ok.
c1672ac4 86iter(T, F1) ->
d10156b0 87 F2 = lift_map(F1),
c1672ac4 88 lists:foreach(F2, T).
03ac148f 89
95c3c2a7
PO
90-spec map(t(K, V), fun((K, V) -> V)) ->
91 t(K, V).
c1672ac4 92map(T, F1) ->
d10156b0
SK
93 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
94 lists:map(F2, T).
03ac148f 95
95c3c2a7
PO
96-spec filter(t(K, V), fun((K, V) -> boolean())) ->
97 t(K, V).
c1672ac4 98filter(T, F1) ->
d10156b0 99 F2 = lift_map(F1),
c1672ac4 100 lists:filter(F2, T).
03ac148f 101
95c3c2a7
PO
102-spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
103 Acc.
03ac148f
SK
104fold(T, F1, Accumulator) ->
105 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
097a2aa3 106 lists:foldl(F2, Accumulator, T).
03ac148f 107
95c3c2a7
PO
108-spec to_kv_list(t(K, V)) ->
109 [{K, V}].
03ac148f
SK
110to_kv_list(T) ->
111 T.
112
95c3c2a7
PO
113-spec of_kv_list([{K, V}]) ->
114 t(K, V).
03ac148f 115of_kv_list(List) ->
37c6e98b 116 % TODO: Decide if validation is to be done here. Do so if yes.
03ac148f
SK
117 List.
118
fa24061d
SK
119-spec validate_unique_presence(t(K, _V), [K]) ->
120 hope_result:t(ok, [presence_error(K)]).
121validate_unique_presence(T, KeysRequired) ->
122 KeysOptional = [],
123 validate_unique_presence(T, KeysRequired, KeysOptional).
124
125-spec validate_unique_presence(t(K, _V), [K], [K]) ->
126 hope_result:t(ok, [presence_error(K)]).
127validate_unique_presence(T, KeysRequired, KeysOptional) ->
128 KeysSupported = KeysRequired ++ KeysOptional,
129 KeysGiven = [K || {K, _V} <- T],
130 KeysGivenUnique = lists:usort(KeysGiven),
131 KeysDups = lists:usort(KeysGiven -- KeysGivenUnique),
132 KeysMissing = KeysRequired -- KeysGivenUnique,
133 KeysUnsupported = KeysGivenUnique -- KeysSupported,
134 case {KeysDups, KeysMissing, KeysUnsupported}
135 of {[], [], []} ->
136 {ok, ok}
137 ; {Dups, Missing, Unsupported} ->
138 ErrorDups =
139 case Dups
140 of [] -> []
141 ; [_|_] -> [{keys_duplicated, Dups}]
142 end,
143 ErrorMissing =
144 case Missing
145 of [] -> []
146 ; [_|_] -> [{keys_missing, Missing}]
147 end,
148 ErrorUnsupported =
149 case Unsupported
150 of [] -> []
151 ; [_|_] -> [{keys_unsupported, Unsupported}]
152 end,
153 Errors = ErrorDups ++ ErrorMissing ++ ErrorUnsupported,
154 {error, Errors}
155 end.
156
03ac148f
SK
157
158%% ============================================================================
159%% Helpers
160%% ============================================================================
161
48371999
SK
162-spec lift_map(F) ->
163 G
164 when F :: fun(( K, V1 ) -> V2)
165 , G :: fun(({K, V1}) -> V2)
166 .
d10156b0
SK
167lift_map(F) ->
168 fun (X) -> apply_map(F, X) end.
169
48371999
SK
170-spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
171 V2.
d10156b0
SK
172apply_map(F, {K, V}) ->
173 F(K, V).
This page took 0.035859 seconds and 4 git commands to generate.