@@ -31,6 +31,70 @@ defmodule Dict do
3131
3232 Dictionaries are required to implement all operations
3333 using the match (`===`) operator.
34+
35+ ## Default implementation
36+
37+ Default implementations for some functions in the `Dict` module
38+ are provided via `use Dict`.
39+
40+ For example:
41+
42+ defmodule MyDict do
43+ use Dict.Behaviour
44+
45+ # implement required functions (see below)
46+ # override default implementations if optimization
47+ # is needed
48+ end
49+
50+ The client module must contain the following functions:
51+
52+ * `delete/2`
53+ * `fetch/2`
54+ * `put/3`
55+ * `reduce/3`
56+ * `size/1`
57+
58+ All functions, except `reduce/3`, are required by the Dict behaviour.
59+ `reduce/3` must be implemtented as per the Enumerable protocol.
60+
61+ Based on these functions, `Dict` generates default implementations
62+ for the following functions:
63+
64+ * `drop/2`
65+ * `equal?/2`
66+ * `fetch!/2`
67+ * `get/2`
68+ * `get/3`
69+ * `has_key?/2`
70+ * `keys/1`
71+ * `merge/2`
72+ * `merge/3`
73+ * `pop/2`
74+ * `pop/3`
75+ * `put_new/3`
76+ * `split/2`
77+ * `take/2`
78+ * `to_list/1`
79+ * `update/4`
80+ * `update!/3`
81+ * `values/1`
82+
83+ All of these functions are defined as overridable, so you can provide
84+ your own implementation if needed.
85+
86+ Note you can also test your custom module via `Dict`'s doctests:
87+
88+ defmodule MyDict do
89+ # ...
90+ end
91+
92+ defmodule MyTests do
93+ use ExUnit.Case
94+ doctest Dict
95+ defp dict_impl, do: MyDict
96+ end
97+
3498 """
3599
36100 use Behaviour
@@ -63,6 +127,146 @@ defmodule Dict do
63127 defcallback update! ( t , key , ( value -> value ) ) :: t | no_return
64128 defcallback values ( t ) :: list ( value )
65129
130+ defmacro __using__ ( _ ) do
131+ # Use this import to guarantee proper code expansion
132+ import Kernel , except: [ size: 1 ]
133+
134+ quote do
135+ @ behaviour Dict
136+
137+ def get ( dict , key , default \\ nil ) do
138+ case fetch ( dict , key ) do
139+ { :ok , value } -> value
140+ :error -> default
141+ end
142+ end
143+
144+ def fetch! ( dict , key ) do
145+ case fetch ( dict , key ) do
146+ { :ok , value } -> value
147+ :error -> raise KeyError , key: key , term: dict
148+ end
149+ end
150+
151+ def has_key? ( dict , key ) do
152+ match? { :ok , _ } , fetch ( dict , key )
153+ end
154+
155+ def put_new ( dict , key , value ) do
156+ case has_key? ( dict , key ) do
157+ true -> dict
158+ false -> put ( dict , key , value )
159+ end
160+ end
161+
162+ def drop ( dict , keys ) do
163+ Enum . reduce ( keys , dict , & delete ( & 2 , & 1 ) )
164+ end
165+
166+ def take ( dict , keys ) do
167+ Enum . reduce ( keys , new , fn key , acc ->
168+ case fetch ( dict , key ) do
169+ { :ok , value } -> put ( acc , key , value )
170+ :error -> acc
171+ end
172+ end )
173+ end
174+
175+ def to_list ( dict ) do
176+ reduce ( dict , { :cont , [ ] } , fn
177+ kv , acc -> { :cont , [ kv | acc ] }
178+ end ) |> elem ( 1 ) |> :lists . reverse
179+ end
180+
181+ def keys ( dict ) do
182+ reduce ( dict , { :cont , [ ] } , fn
183+ { k , _ } , acc -> { :cont , [ k | acc ] }
184+ end ) |> elem ( 1 ) |> :lists . reverse
185+ end
186+
187+ def values ( dict ) do
188+ reduce ( dict , { :cont , [ ] } , fn
189+ { _ , v } , acc -> { :cont , [ v | acc ] }
190+ end ) |> elem ( 1 ) |> :lists . reverse
191+ end
192+
193+ def equal? ( dict1 , dict2 ) do
194+ # Use this import to avoid conflicts in the user code
195+ import Kernel , except: [ size: 1 ]
196+
197+ case size ( dict1 ) == size ( dict2 ) do
198+ false -> false
199+ true ->
200+ reduce ( dict1 , { :cont , true } , fn ( { k , v } , _acc ) ->
201+ case fetch ( dict2 , k ) do
202+ { :ok , ^ v } -> { :cont , true }
203+ _ -> { :halt , false }
204+ end
205+ end ) |> elem ( 1 )
206+ end
207+ end
208+
209+ def merge ( dict1 , dict2 , fun \\ fn ( _k , _v1 , v2 ) -> v2 end ) do
210+ # Use this import to avoid conflicts in the user code
211+ import Kernel , except: [ size: 1 ]
212+
213+ if size ( dict1 ) < size ( dict2 ) do
214+ reduce ( dict1 , { :cont , dict2 } , fn { k , v1 } , acc ->
215+ { :cont , update ( acc , k , v1 , & fun . ( k , v1 , & 1 ) ) }
216+ end )
217+ else
218+ reduce ( dict2 , { :cont , dict1 } , fn { k , v2 } , acc ->
219+ { :cont , update ( acc , k , v2 , & fun . ( k , & 1 , v2 ) ) }
220+ end )
221+ end |> elem ( 1 )
222+ end
223+
224+ def update ( dict , key , initial , fun ) do
225+ case fetch ( dict , key ) do
226+ { :ok , value } ->
227+ put ( dict , key , fun . ( value ) )
228+ :error ->
229+ put ( dict , key , initial )
230+ end
231+ end
232+
233+ def update! ( dict , key , fun ) do
234+ case fetch ( dict , key ) do
235+ { :ok , value } ->
236+ put ( dict , key , fun . ( value ) )
237+ :error ->
238+ raise KeyError , key: key , term: dict
239+ end
240+ end
241+
242+ def pop ( dict , key , default \\ nil ) do
243+ case fetch ( dict , key ) do
244+ { :ok , value } ->
245+ { value , delete ( dict , key ) }
246+ :error ->
247+ { default , dict }
248+ end
249+ end
250+
251+ def split ( dict , keys ) do
252+ Enum . reduce ( keys , { new , dict } , fn key , { inc , exc } = acc ->
253+ case fetch ( exc , key ) do
254+ { :ok , value } ->
255+ { put ( inc , key , value ) , delete ( exc , key ) }
256+ :error ->
257+ acc
258+ end
259+ end )
260+ end
261+
262+ defoverridable merge: 2 , merge: 3 , equal?: 2 , to_list: 1 , keys: 1 ,
263+ values: 1 , take: 2 , drop: 2 , get: 2 , get: 3 , fetch!: 2 ,
264+ has_key?: 2 , put_new: 3 , pop: 2 , pop: 3 , split: 2 ,
265+ update: 4 , update!: 3
266+ end
267+ end
268+
269+
66270 defmacrop target ( dict ) do
67271 quote do
68272 case unquote ( dict ) do
0 commit comments