1
+ ; -
2
+ ; Copyright 2008-2011 (c) Meikel Brandmeyer.
3
+ ; All rights reserved.
4
+ ;
5
+ ; Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ ; of this software and associated documentation files (the "Software"), to deal
7
+ ; in the Software without restriction, including without limitation the rights
8
+ ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ ; copies of the Software, and to permit persons to whom the Software is
10
+ ; furnished to do so, subject to the following conditions:
11
+ ;
12
+ ; The above copyright notice and this permission notice shall be included in
13
+ ; all copies or substantial portions of the Software.
14
+ ;
15
+ ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ ; THE SOFTWARE.
22
+
23
+ ; ; Retreived from https://bitbucket.org/kotarak/lazymap/raw/5a2437e70a91/src/main/clojure/lazymap/core.clj
24
+ ; ; by JW on 11/17/2011, and slightly modified to fix a bug in .entryAt and add a safety check.
25
+ ; ; Also replaced 'force' with 'deref' so this can be used with futures (with marginal utility).
26
+ ; ; (our pull request with these changes has been been ignored, so we've just included
27
+ ; ; this fixed file in plumbing for the time being).
28
+
29
+ ; ; Known Issues: LazyMapEntries are not equal to persistent vectors like ordinary map entries.
30
+
31
+
32
+ (ns plumbing.lazymap
33
+ " Lazymap is to maps what lazy-seq is to lists. It allows to store values
34
+ with evaluating them. This is only done in case the value is really accessed.
35
+ Lazymap works with any map type (hash-map, sorted-map, struct-map) and may
36
+ be used as a drop-in replacement everywhere where a normal map type may be
37
+ used.
38
+ Available macros:
39
+ lazy-hash-map, lazy-sorted-map, lazy-struct-map, lazy-struct, lazy-assoc
40
+ and their * counterpart functions."
41
+ (:import
42
+ clojure.lang.IObj
43
+ clojure.lang.IFn
44
+ clojure.lang.ILookup
45
+ clojure.lang.IMapEntry
46
+ clojure.lang.IPersistentMap
47
+ clojure.lang.IPersistentVector
48
+ clojure.lang.ASeq
49
+ clojure.lang.ISeq
50
+ clojure.lang.Seqable
51
+ clojure.lang.SeqIterator))
52
+
53
+ (defprotocol ILazyMapEntry
54
+ " ILazyMapEntry describes the behaviour of a lazy MapEntry. It provides
55
+ an additional method (over IMapEntry), which returns the raw delay object
56
+ and not the forced value."
57
+ (get-key [lazy-map-entry] " Return the key of the map entry." )
58
+ (get-raw-value [lazy-map-entry] " Return the underlying delay object." ))
59
+
60
+ ; Extend the IMapEntry interface to act also like ILazyMapEntry.
61
+ ; For a IMapEntry get-raw-value just returns the given value as
62
+ ; wrapped in a delay. Similar a vector of two elements might be
63
+ ; used in place of a IMapEntry.
64
+ (extend-protocol ILazyMapEntry
65
+ IMapEntry
66
+ (get-key [#^IMapEntry this] (.getKey this))
67
+ (get-raw-value [#^IMapEntry this] (let [v (.getValue this)] (delay v)))
68
+ IPersistentVector
69
+ (get-key [this]
70
+ (when-not (= (count this) 2 )
71
+ (throw (IllegalArgumentException.
72
+ " Vector used as IMapEntry must be a pair" )))
73
+ (this 0 ))
74
+ (get-raw-value
75
+ [this]
76
+ (when-not (= (count this) 2 )
77
+ (throw (IllegalArgumentException.
78
+ " Vector used as IMapEntry must be a pair" )))
79
+ (let [v (this 1 )]
80
+ (delay v))))
81
+
82
+ (defprotocol ILazyPersistentMap
83
+ " ILazyPersistentMap extends IPersistentMap with a method to allow
84
+ transportation of the underlying delay objects."
85
+ (delay-assoc [lazy-map key delay] " Associates the given delay in the map." ))
86
+
87
+ (deftype LazyMapEntry [k v]
88
+ ILazyMapEntry
89
+ (get-key [_] k)
90
+ (get-raw-value [_] v)
91
+ IMapEntry
92
+ (key [_] k)
93
+ (getKey [_] k)
94
+ (val [_] (deref v))
95
+ (getValue [_] (deref v))
96
+ Object
97
+ (toString [_] (str \[ (pr-str k) \space (pr-str (deref v)) \])))
98
+
99
+ (defmethod print-method LazyMapEntry
100
+ [this #^java.io.Writer w]
101
+ (.write w (str this)))
102
+
103
+ (defn create-lazy-map-seq
104
+ ([inner-seq]
105
+ (create-lazy-map-seq inner-seq nil ))
106
+ ([inner-seq metadata]
107
+ (proxy [ASeq] [metadata]
108
+ ; ISeq
109
+ (first []
110
+ (let [first-val (first inner-seq)]
111
+ (LazyMapEntry. (key first-val) (val first-val))))
112
+ (next []
113
+ (when-let [inner-rest (next inner-seq)]
114
+ (create-lazy-map-seq inner-rest metadata)))
115
+ (more [] (lazy-seq (next this))))))
116
+
117
+ (declare create-lazy-map )
118
+
119
+ (deftype LazyPersistentMap
120
+ [base metadata]
121
+ ILazyPersistentMap
122
+ (delay-assoc [this k v] (create-lazy-map (assoc base k v) metadata))
123
+ IPersistentMap
124
+ (assoc [this k v] (create-lazy-map (assoc base k (delay v)) metadata))
125
+ (assocEx
126
+ [this k v]
127
+ (when (contains? base k)
128
+ (throw (Exception. (str " Key already present in map: " k))))
129
+ (.assoc this k v))
130
+ (without [this k] (create-lazy-map (dissoc base k) metadata))
131
+ ; Associative
132
+ (containsKey [this k] (contains? base k))
133
+ (entryAt [this k] (when-let [v (base k)] (LazyMapEntry. k v)))
134
+ ; IPersistentCollection
135
+ (count [this] (count base))
136
+ (cons
137
+ [this o]
138
+ (if (satisfies? ILazyMapEntry o)
139
+ (delay-assoc this (get-key o) (get-raw-value o))
140
+ (into this o)))
141
+ (empty [this] (create-lazy-map (empty base) nil ))
142
+ ILookup
143
+ (valAt [this k] (.valAt this k nil ))
144
+ (valAt
145
+ [this k not-found]
146
+ (if (contains? base k)
147
+ (-> base (get k) deref)
148
+ not-found))
149
+ IFn
150
+ (invoke [this k] (.valAt this k nil ))
151
+ (invoke [this k not-found] (.valAt this k not-found))
152
+ (applyTo
153
+ [this args]
154
+ (let [[k v & rest-args :as args] (seq args)]
155
+ (when (or (not args) rest-args)
156
+ (throw (Exception. " lazy map must be called with one or two arguments" )))
157
+ (.valAt this k v)))
158
+ Seqable
159
+ (seq
160
+ [this]
161
+ (when-let [inner-seq (seq base)]
162
+ (create-lazy-map-seq inner-seq)))
163
+ IObj
164
+ (withMeta [this new-metadata] (create-lazy-map base new-metadata))
165
+ ; IMeta
166
+ (meta [this] metadata)
167
+ Iterable
168
+ (iterator [this] (-> this .seq SeqIterator.)))
169
+
170
+ (defn create-lazy-map
171
+ ([base]
172
+ (create-lazy-map base nil ))
173
+ ([base metadata]
174
+ (LazyPersistentMap. base metadata)))
175
+
176
+ (defn- quote-values
177
+ [kvs]
178
+ (assert (even? (count kvs)))
179
+ (mapcat (fn [[k v]] [k `(delay ~v)]) (partition 2 kvs)))
180
+
181
+ (defn lazy-assoc*
182
+ " lazy-assoc* is like lazy-assoc but a function and takes values as delays
183
+ instead of expanding into a delay of val."
184
+ [m & kvs]
185
+ (assert (even? (count kvs)))
186
+ (reduce (fn [m [k v]] (delay-assoc m k v)) m (partition 2 kvs)))
187
+
188
+ (defmacro lazy-assoc
189
+ " lazy-assoc associates new values to the given keys in the given lazy map.
190
+ The values are not evaluated, before their first retrieval. They are
191
+ evaluated at most once."
192
+ [m & kvs]
193
+ `(lazy-assoc* ~m ~@(quote-values kvs)))
194
+
195
+ (defn lazy-hash-map*
196
+ " lazy-hash-map* is the same as lazy-hash-map except that its a function
197
+ and it takes a seq of keys-delayed-value pairs."
198
+ [& kvs]
199
+ (create-lazy-map (apply hash-map kvs)))
200
+
201
+ (defmacro lazy-hash-map
202
+ " lazy-hash-map creates a map. The values are not evaluated before their
203
+ first retrieval. Each value is evaluated at most once. The underlying map
204
+ is a hash map."
205
+ [& kvs]
206
+ `(lazy-hash-map* ~@(quote-values kvs)))
207
+
208
+ (defn lazy-sorted-map*
209
+ " lazy-sorted-map* is the same as lazy-sorted-map except that its a
210
+ function and it takes a seq of keys-delayed-value pairs."
211
+ [& kvs]
212
+ (create-lazy-map (apply sorted-map kvs)))
213
+
214
+ (defmacro lazy-sorted-map
215
+ " lazy-sorted-map creates a map. The values are not evaluated before their
216
+ first retrieval. Each value is evaluated at most once. The underlying map
217
+ is a sorted map."
218
+ [& kvs]
219
+ `(lazy-sorted-map* ~@(quote-values kvs)))
220
+
221
+ (defn lazy-struct-map*
222
+ " lazy-struct-map* is the same as lazy-struct-map except that its a
223
+ function and it takes a seq of keys-delayed-value pairs together with the
224
+ struct basis."
225
+ [s & kvs]
226
+ (create-lazy-map (apply struct-map s kvs)))
227
+
228
+ (defmacro lazy-struct-map
229
+ " lazy-struct-map creates a map. The values are not evaluated before their
230
+ first retrieval. Each value is evaluated at most once. The underlying map
231
+ is a struct map according to the provided structure s."
232
+ [s & kvs]
233
+ `(lazy-struct-map* ~s ~@(quote-values kvs)))
234
+
235
+ (defn lazy-struct*
236
+ " lazy-struct* is the same as lazy-struct except that its a function and
237
+ it takes a seq of delayed value together with the struct basis."
238
+ [s & vs]
239
+ (create-lazy-map (apply struct s vs)))
240
+
241
+ (defmacro lazy-struct
242
+ " lazy-struct creates a map. The values are not evaluated before their
243
+ first retrieval. Each value is evaluated at most once. The underlying map
244
+ is a struct map according to the provided structure s. As with Clojure's
245
+ struct the values have to appear in the order of the keys in the structure."
246
+ [s & vs]
247
+ (let [vs (map (fn [v] `(delay ~v)) vs)]
248
+ `(lazy-struct* ~s ~@vs)))
0 commit comments