Add get with default to dictionary interface.
[hope.git] / src / hope_kv_list.erl
1 %%%----------------------------------------------------------------------------
2 %%% Equivalent to stdlib's orddict, but with a pretty (IMO), uniform interface.
3 %%%----------------------------------------------------------------------------
4 -module(hope_kv_list).
5
6 -behavior(hope_gen_dictionary).
7
8 -export_type(
9 [ t/2
10 ]).
11
12 -export(
13 [ empty/0
14 , get/2
15 , get/3
16 , set/3
17 , update/3
18 , pop/2
19 , iter/2
20 , map/2
21 , filter/2
22 , fold/3
23 , of_kv_list/1
24 , to_kv_list/1
25 , validate_unique_presence/2 % Assume default optional parameter(s)
26 , validate_unique_presence/3 % Specify optional parameter(s)
27 ]).
28
29
30 -type t(K, V) ::
31 [{K, V}].
32
33 -type presence_error(A) ::
34 {keys_missing , [A]}
35 | {keys_duplicated , [A]}
36 | {keys_unsupported , [A]}
37 .
38
39
40 %% ============================================================================
41 %% API
42 %% ============================================================================
43
44 -spec empty() ->
45 [].
46 empty() ->
47 [].
48
49 -spec get(t(K, V), K) ->
50 hope_option:t(V).
51 get(T, K) ->
52 case lists:keyfind(K, 1, T)
53 of false -> none
54 ; {K, V} -> {some, V}
55 end.
56
57 -spec get(t(K, V), K, V) ->
58 V.
59 get(T, K, Default) ->
60 Vopt = get(T, K),
61 hope_option:get(Vopt, Default).
62
63 -spec set(t(K, V), K, V) ->
64 t(K, V).
65 set(T, K, V) ->
66 lists:keystore(K, 1, T, {K, V}).
67
68 -spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
69 t(K, V).
70 update(T, K, F) ->
71 V1Opt = get(T, K),
72 V2 = F(V1Opt),
73 % TODO: Eliminate the 2nd lookup.
74 set(T, K, V2).
75
76 -spec pop(t(K, V), K) ->
77 {hope_option:t(V), t(K, V)}.
78 pop(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
84 -spec iter(t(K, V), fun((K, V) -> ok)) ->
85 ok.
86 iter(T, F1) ->
87 F2 = lift_map(F1),
88 lists:foreach(F2, T).
89
90 -spec map(t(K, V), fun((K, V) -> V)) ->
91 t(K, V).
92 map(T, F1) ->
93 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
94 lists:map(F2, T).
95
96 -spec filter(t(K, V), fun((K, V) -> boolean())) ->
97 t(K, V).
98 filter(T, F1) ->
99 F2 = lift_map(F1),
100 lists:filter(F2, T).
101
102 -spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
103 Acc.
104 fold(T, F1, Accumulator) ->
105 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
106 lists:foldl(F2, Accumulator, T).
107
108 -spec to_kv_list(t(K, V)) ->
109 [{K, V}].
110 to_kv_list(T) ->
111 T.
112
113 -spec of_kv_list([{K, V}]) ->
114 t(K, V).
115 of_kv_list(List) ->
116 % TODO: Decide if validation is to be done here. Do so if yes.
117 List.
118
119 -spec validate_unique_presence(t(K, _V), [K]) ->
120 hope_result:t(ok, [presence_error(K)]).
121 validate_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)]).
127 validate_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
157
158 %% ============================================================================
159 %% Helpers
160 %% ============================================================================
161
162 -spec lift_map(F) ->
163 G
164 when F :: fun(( K, V1 ) -> V2)
165 , G :: fun(({K, V1}) -> V2)
166 .
167 lift_map(F) ->
168 fun (X) -> apply_map(F, X) end.
169
170 -spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
171 V2.
172 apply_map(F, {K, V}) ->
173 F(K, V).
This page took 0.060127 seconds and 4 git commands to generate.