Source

lazymap / src / main / clojure / de / kotka / lazymap.clj

Full commit
Meikel  Brandmey… 06c7f8a 























































Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 



Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 








Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 

Meikel  Brandmey… 06c7f8a 

Meikel  Brandmey… accb3b6 

Meikel  Brandmey… 06c7f8a 

Meikel  Brandmey… accb3b6 

Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 


Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 




Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 


Meikel  Brandmey… 06c7f8a 


Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 

Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 


Meikel  Brandmey… 06c7f8a 















Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 


Meikel  Brandmey… 06c7f8a 



Meikel  Brandmey… accb3b6 



Meikel  Brandmey… 06c7f8a 




Meikel  Brandmey… accb3b6 

Meikel  Brandmey… 06c7f8a 


Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 
Meikel  Brandmey… accb3b6 
Meikel  Brandmey… 06c7f8a 







































































;-
; Copyright 2008,2009 (c) Meikel Brandmeyer.
; All rights reserved.
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.

(clojure.core/ns de.kotka.lazymap
  "Lazymap is to maps what lazy-seq is to lists. It allows to store values
  with evaluating them. This is only done in case the value is really accessed.
  Lazymap works with any map type (hash-map, sorted-map, struct-map) and may
  be used as a drop-in replacement everywhere where a normal map type may be
  used.

  Available macros:
  lazy-hash-map, lazy-sorted-map, lazy-struct-map, lazy-struct, lazy-assoc
  and their * counterpart functions."
  (:import
     clojure.lang.IObj
     clojure.lang.IFn
     clojure.lang.IMapEntry
     clojure.lang.IPersistentVector
     clojure.lang.ISeq
     clojure.lang.SeqIterator))

(gen-interface
  :name    de.kotka.lazymap.ILazyMapEntry
  :extends [clojure.lang.IMapEntry]
  :methods [[getRawValue [] clojure.lang.Delay]])

(gen-interface
  :name    de.kotka.lazymap.ILazyMap
  :extends [clojure.lang.IPersistentMap]
  :methods [[lazyAssoc [Object Object] de.kotka.lazymap.ILazyMap]])

(import
  'de.kotka.lazymap.ILazyMapEntry
  'de.kotka.lazymap.ILazyMap)

(defn create-lazy-map-entry
  [k v]
  (reify [ILazyMapEntry]
    ; ILazyMapEntry
    (getRawValue [this] v)
    ; IMapEntry
    (key         [this] (.getKey this))
    (getKey      [this] k)
    (val         [this] (.getValue this))
    (getValue    [this] (force v))
    ; Object
    (toString    [this] (str \[ (.getKey this) \space (.getValue this) \]))))

(defmethod print-method ILazyMapEntry
  [#^ILazyMapEntry this #^java.io.Writer writer]
  (.write writer (.toString this)))

(defn create-lazy-map-seq
  ([inner-seq]
   (create-lazy-map-seq inner-seq nil))
  ([inner-seq metadata]
   (reify [ISeq IObj]
     ; ISeq
     (first
       [this]
       (let [first-val (first inner-seq)]
         (create-lazy-map-entry (key first-val) (val first-val))))
     (next
       [this]
       (when-let [inner-rest (next inner-seq)]
         (create-lazy-map-seq inner-rest metadata)))
     (more  [this]   (lazy-seq (.next this)))
     (cons  [this o] (cons o this))
     ; IPersistentCollection
     (count [this]   (count inner-seq))
     (empty [this]   ())
     (seq   [this]   this)
     ; IObj
     (withMeta [this new-metadata] (create-lazy-map-seq inner-seq new-metadata))
     ;IMeta
     (meta  [this]   metadata))))

(defn create-lazy-map
  ([base]
   (create-lazy-map base nil))
  ([base metadata]
   (reify [ILazyMap IFn IObj]
     ; ILazyMap
     (lazyAssoc [this k v] (create-lazy-map (assoc base k v) metadata))
     ; IPersistentMap
     (assoc     [this k v] (create-lazy-map (assoc base k (delay v)) metadata))
     (assocEx
       [this k v]
       (when (.containsKey base k)
         (throw (Exception. (str "Key already present in map: " k))))
       (.assoc this k v))
     (without     [this k] (create-lazy-map (dissoc base k) metadata))
     ; Associative
     (containsKey [this k] (contains? base k))
     (entryAt     [this k] (create-lazy-map-entry k (base k)))
     ; IPersistentCollection
     (count       [this]   (count base))
     (cons
       [this o]
       (condp instance? o
         ILazyMapEntry     (let [#^ILazyMapEntry o o]
                             (let [k (.getKey o)
                                   v (.getRawValue o)]
                               (.lazyAssoc this k v)))
         IMapEntry         (let [#^IMapEntry o o]
                             (let [k (.getKey o)
                                   v (.getValue o)]
                               (.assoc this k (delay v))))
         IPersistentVector (if (= (count o) 2)
                             (let [k (o 0)
                                   v (o 1)]
                               (.assoc this k (delay v)))
                             (throw (IllegalArgumentException.
                                      "Vector arg to map conj must be a pair")))
         (reduce #(.cons #^ILazyMap %1 %2) this o)))
     (empty [this]   (create-lazy-map (empty base) nil))
     ; ILookup
     (valAt [this k] (.valAt this k nil))
     (valAt
       [this k not-found]
       (if (contains? base k)
         (-> base (get k) force)
         not-found))
     ; IFn
     (invoke [this k]           (.valAt this k nil))
     (invoke [this k not-found] (.valAt this k not-found))
     (applyTo
       [this args]
       (let [[k v & rest-args :as args] (seq args)]
         (when (or (not args) rest-args)
           (throw (Exception. "lazy map must be called with one or two arguments")))
         (.valAt this k v)))
     ; Seqable
     (seq
       [this]
       (when-let [inner-seq (seq base)]
         (create-lazy-map-seq inner-seq)))
     ; IObj
     (withMeta [this new-metadata] (create-lazy-map base new-metadata))
     ; IMeta
     (meta     [this] metadata)
     ; Iterable
     (iterator [this] (-> this .seq SeqIterator.)))))

(defn- quote-values
  [kvs]
  (mapcat (fn [[k v]] [k `(delay ~v)]) (partition 2 kvs)))

(defn lazy-assoc*
  "lazy-assoc* is like lazy-assoc but a function and takes values as delays
  instead of expanding into a delay of val."
  [m & kvs]
  (assert (even? (count kvs)))
  (reduce (fn [m [k v]] (.lazyAssoc m k v)) m (partition 2 kvs)))

(defmacro lazy-assoc
  "lazy-assoc associates new values to the given keys in the given lazy map.
  The values are not evaluated, before their first retrieval. They are
  evaluated at most once."
  [m & kvs]
  `(lazy-assoc* ~m ~@(quote-values kvs)))

(defn lazy-hash-map*
  "lazy-hash-map* is the same as lazy-hash-map except that its a function
  and it takes a seq of keys-delayed-value pairs."
  [& kvs]
  (create-lazy-map (apply hash-map kvs)))

(defmacro lazy-hash-map
  "lazy-hash-map creates a map. The values are not evaluated before their
  first retrieval. Each value is evaluated at most once. The underlying map
  is a hash map."
  [& kvs]
  `(lazy-hash-map* ~@(quote-values kvs)))

(defn lazy-sorted-map*
  "lazy-sorted-map* is the same as lazy-sorted-map except that its a
  function and it takes a seq of keys-delayed-value pairs."
  [& kvs]
  (create-lazy-map (apply sorted-map kvs)))

(defmacro lazy-sorted-map
  "lazy-sorted-map creates a map. The values are not evaluated before their
  first retrieval. Each value is evaluated at most once. The underlying map
  is a sorted map."
  [& kvs]
  `(lazy-sorted-map* ~@(quote-values kvs)))

(defn lazy-struct-map*
  "lazy-struct-map* is the same as lazy-struct-map except that its a
  function and it takes a seq of keys-delayed-value pairs together with the
  struct basis."
  [s & kvs]
  (create-lazy-map (apply struct-map s kvs)))

(defmacro lazy-struct-map
  "lazy-struct-map creates a map. The values are not evaluated before their
  first retrieval. Each value is evaluated at most once. The underlying map
  is a struct map according to the provided structure s."
  [s & kvs]
  `(lazy-struct-map* ~s ~@(quote-values kvs)))

(defn lazy-struct*
  "lazy-struct* is the same as lazy-struct except that its a function and
  it takes a seq of delayed value together with the struct basis."
  [s & vs]
  (create-lazy-map (apply struct s vs)))

(defmacro lazy-struct
  "lazy-struct creates a map. The values are not evaluated before their
  first retrieval. Each value is evaluated at most once. The underlying map
  is a struct map according to the provided structure s. As with Clojure's
  struct the values have to appear in the order of the keys in the structure."
  [s & vs]
  (let [vs (map (fn [v] `(delay ~v)) vs)]
    `(lazy-struct* ~s ~@vs)))