Commits

Meikel Brandmeyer committed 5a2437e Merge

Merge new back into default

  • Participants
  • Parent commits c531805, 9e00671

Comments (0)

Files changed (17)

+syntax: glob
+
+build/*
+.gradle
+local.gradle
 db654f5e445aa879b31b827e61681f11645c80ec v2.0.1
 62119ab9a5a30112be4611a3c56e7d1d4f336aef v2.1.0
 7623099c8d1790d8a82f17a0aea77f2291ead5ca v2.2.0
+286adbe940fa51ba95cfaf4d96d2691dc6512944 v3.0.0
-Copyright 2008 (c) Meikel Brandmeyer.
+Copyright 2008-2011 (c) Meikel Brandmeyer.
 All rights reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy

File README.markdown

+# lazymap – take the strictness out of maps
+
+## Description
+
+LazyMap is a transparent wrapper around the map types of Clojure. It works
+similar to lazy-seq for sequences: the value associated with a key via
+lazy-assoc is not evaluated until it is actually accessed. So if the value is
+dissoc'd before it is accessed the code necessary to generate the value is not
+executed at all.
+
+## Usage
+
+    user=> (use 'lazymap.core)
+    nil
+    user=> (def m (lazy-hash-map :x (do (println :a) :y)))
+    #'user/m
+    user=> (get m :x)
+    :a
+    :y
+    user=> (get m :x)
+    :y
+    user=>
+
+### Gradle
+
+    dependencies { compile 'de.kotka:lazymap:3.0.0' }
+
+### Maven
+
+    <dependency>
+      <groupId>de.kotka</groupId>
+      <artifactId>lazymap</artifactId>
+      <version>3.0.0</version>
+    </dependency>
+
+### Leiningen
+
+    :dependencies [[de.kotka/lazymap "3.0.0"]]
+
+Meikel Brandmeyer <mb@kotka.de>,<br>
+Frankfurt am Main, 2011

File README.txt

-          ______                     ______  ___
-          ___  / ______ __________  ____   |/  /_____ ________
-          __  /  _  __ `/__  /_  / / /_  /|_/ /_  __ `/__  __ \
-          _  /___/ /_/ /__  /_  /_/ /_  /  / / / /_/ /__  /_/ /
-          /_____/\__,_/ _____/\__, / /_/  /_/  \__,_/ _  .___/
-                             /____/                   /_/
-
-LazyMap is a transparent wrapper around the map types of Clojure. It works
-similar to lazy-seq for sequences: the value associated with a key via
-lazy-assoc is not evaluated until it is actually accessed. So if the value is
-dissoc'd before it is accessed the code necessary to generate the value is not
-executed at all.
-
-To build lazy-map create a file "local.properties". Put a line in that file,
-which specifies the location of your clojure.jar file.
-
-clojure.jar=/path/to/your/clojure.jar
-
-Then simply build by invoking ant.
-
-Put the jar in your classpath and (require) the de.kotka.lazymap namespace.
-Further documentation can be obtained via find-doc and doc in the Repl.
-
-Note: This release of LazyMap is compatible with (and hence needs a) post
-lazy-branch merge Clojure.
-
-Meikel Brandmeyer <mb@kotka.de>,
-Frankfurt am Main, 2009

File build.gradle

+buildscript {
+    repositories { flatDir dirs: project.file('lib') }
+    dependencies {
+        classpath "clojuresque:clojuresque:1.4.1"
+        classpath "local-dotgradle:local-dotgradle:1.0.0"
+    }
+}
+
+apply plugin: 'clojure'
+apply plugin: 'local-dotgradle'
+
+group = 'de.kotka'
+version = '3.0.0'
+
+defaultRepositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'org.clojure:clojure:1.2.1'
+}
+
+uploadArchives {
+    clojarsDeploy()
+}

File build.xml

-<project name="lazymap" default="artifacts"
-    xmlns:ivy="antlib:org.apache.ivy.ant">
-
-    <description>
-        lazymap is an implementation of a lazy map for the Clojure
-        programming language. It is to maps what lazy-seq is for
-        sequences. You can use Ivy to depend on the project. Put
-        the following in your ivy.xml:
-        &lt;dependency org="de.kotka" name="lazymap"/&gt;
-
-        In your ivysettings.xml put the following:
-        &lt;resolvers&gt;
-            &lt;url name="Kotka"&gt;
-                &lt;ivy pattern="http://kotka.de/ivy/[organisation]/[module]/[revision]/ivys/ivy.xml"/&gt;
-                &lt;artifact pattern="http://kotka.de/ivy/[organisation]/[module]/[revision]/jars/[artifact].[ext]"/&gt;
-            &lt;/url&gt;
-        &lt;/resolvers&gt;
-
-        &lt;modules&gt;
-            &lt;module organisation="de.kotka" name="lazymap" resolver="Kotka"/&gt;
-        &lt;/modules&gt;
-    </description>
-
-    <property name="src.dir" location="src"/>
-    <property name="classes.dir" location="classes"/>
-    <property name="build.dir" location="build"/>
-    <property name="lib.dir" location="lib"/>
-
-    <property name="jar.file" location="${build.dir}/lazymap.jar"/>
-    <property name="srcjar.file" location="${build.dir}/lazymap-source.jar"/>
-
-    <property name="current.version" value="2.2"/>
-
-    <property file="local.properties"/>
-
-    <target name="init" description="--> create build directories">
-        <tstamp/>
-        <mkdir dir="${classes.dir}"/>
-        <mkdir dir="${build.dir}"/>
-        <mkdir dir="${lib.dir}"/>
-    </target>
-
-    <target name="resolve" depends="init"
-        description="--> resolve dependencies with Ivy">
-        <ivy:retrieve />
-        <ivy:resolve />
-    </target>
-
-    <target name="aot" depends="resolve,init"
-        description="--> AOT compile clojure sources">
-        <java classname="clojure.lang.Compile">
-            <classpath>
-                <path location="${classes.dir}"/>
-                <path location="${src.dir}"/>
-                <fileset dir="${lib.dir}" includes="**/*.jar"/>
-            </classpath>
-            <sysproperty key="clojure.compile.path" value="${classes.dir}"/>
-            <arg value="de.kotka.lazymap.LazyMapEntry"/>
-            <arg value="de.kotka.lazymap.LazyMapSeq"/>
-            <arg value="de.kotka.lazymap.LazyMap"/>
-            <arg value="de.kotka.lazymap"/>
-        </java>
-    </target>
-
-    <target name="artifacts" depends="aot"
-        description="--> create source and artifact jars">
-        <jar jarfile="${jar.file}">
-            <fileset file="README.txt"/>
-            <fileset file="LICENSE.txt"/>
-            <fileset dir="${classes.dir}" includes="de/kotka/**/*.class"/>
-            <manifest>
-                <attribute name="Class-Path" value="."/>
-            </manifest>
-        </jar>
-
-        <jar jarfile="${srcjar.file}">
-            <fileset file="README.txt"/>
-            <fileset file="LICENSE.txt"/>
-            <fileset dir="${src.dir}" includes="**/*.clj"/>
-        </jar>
-    </target>
-
-    <target name="clean" description="--> clean generated files">
-        <delete dir="${classes.dir}"/>
-        <delete dir="${build.dir}"/>
-    </target>
-
-    <target name="clean-lib" description="--> clean library files">
-        <delete dir="${lib.dir}"/>
-    </target>
-
-    <target name="clean-local" description="--> clean local repository">
-        <ivy:info />
-        <delete dir="${ivy.local.default.root}/${ivy.organisation}/${ivy.module}"/>
-    </target>
-
-    <target name="clean-all" depends="clean,clean-lib,clean-local"
-        description="--> clean all project files"/>
-
-    <target name="publish"
-        description="--> publish artifacts in the shared repository">
-        <ivy:info />
-        <ivy:buildnumber
-            organisation="${ivy.organisation}"
-            module="${ivy.module}"
-            revision="${current.version}"/>
-        <ivy:publish
-            resolver="shared"
-            artifactspattern="${build.dir}/[artifact].[ext]"
-            pubrevision="${ivy.new.revision}"
-            status="release"/>
-    </target>
-
-    <target name="publish-local"
-        description="--> publish artifacts in the local repository">
-        <tstamp>
-            <format property="now" pattern="yyyyMMddHHmmss"/>
-        </tstamp>
-        <ivy:info />
-        <ivy:publish
-            resolver="local"
-            artifactspattern="${build.dir}/[artifact].[ext]"
-            pubrevision="${now}"
-            pubdate="${now}"
-            status="integration"
-            forcedeliver="true"/>
-    </target>
-
-</project>

File ivy.xml

-<ivy-module version="2.0">
-
-    <info organisation="de.kotka" module="lazymap" status="integration"/>
-
-    <configurations>
-        <conf name="default"/>
-        <conf name="dev" extends="default"/>
-    </configurations>
-
-    <publications>
-        <artifact name="lazymap" type="jar" conf="default"/>
-        <artifact name="lazymap-source" type="jar" conf="dev"/>
-    </publications>
-
-    <dependencies>
-        <dependency org="org.clojure" name="clojure-lang" rev="1.0.0"
-            conf="*->default"/>
-    </dependencies>
-
-</ivy-module>

File ivysettings.xml

-<ivysettings>
-
-    <!-- Load the standard Ivy settings. Which just extend them for Clojure. -->
-    <include url="${ivy.default.settings.dir}/ivysettings.xml"/>
-    <include url="http://kotka.de/ivy/ivysettings.xml"/>
-
-</ivysettings>

File lib/clojuresque-1.4.1.jar

Binary file added.

File lib/local-dotgradle-1.0.0.jar

Binary file added.

File settings.gradle

+rootProject.name = 'lazymap'

File src/de/kotka/lazymap.clj

-;-
-; 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
-     (de.kotka.lazymap LazyMap)))
-
-(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]
-  (LazyMap. (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]
-  (LazyMap. (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]
-  (LazyMap. (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]
-  (LazyMap. (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)))

File src/de/kotka/lazymap/LazyMap.clj

-;-
-; 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
-  (:gen-class
-     :init         init
-     :state        theMap
-     :extends      clojure.lang.APersistentMap
-     :methods      [[lazyAssoc [Object Object] clojure.lang.IPersistentMap]]
-     :constructors {[clojure.lang.IPersistentMap] []
-                    [clojure.lang.IPersistentMap clojure.lang.IPersistentMap]
-                    [clojure.lang.IPersistentMap]})
-  (:import
-     (clojure.lang SeqIterator IMapEntry IPersistentVector IObj)))
-
-(defn- -init
-  ([theMap]
-   [[] theMap])
-  ([theMap metaData]
-   [[metaData] theMap]))
-
-(defn- -lazyAssoc
-  [#^de.kotka.lazymap.LazyMap this k v]
-  (de.kotka.lazymap.LazyMap. (-> this .theMap (assoc k v))))
-
-; IObj
-(defn- -withMeta
-  [#^de.kotka.lazymap.LazyMap this meta-data]
-  (de.kotka.lazymap.LazyMap. (.theMap this) meta-data))
-
-; IPersistentMap
-(defn- -assoc
-  [#^de.kotka.lazymap.LazyMap this k v]
-  (de.kotka.lazymap.LazyMap. (-> this .theMap (assoc k (delay v)))))
-
-(defn- -assocEx
-  [#^de.kotka.lazymap.LazyMap this k v]
-  (when (.containsKey this k)
-    (throw (Exception. (str "Key alread present: " k))))
-  (.assoc this k v))
-
-(defn- -without
-  [#^de.kotka.lazymap.LazyMap this k]
-  (-> this .theMap (dissoc k) de.kotka.lazymap.LazyMap.))
-
-; Associative
-(defn- -containsKey
-  [#^de.kotka.lazymap.LazyMap this k]
-  (-> this .theMap (contains? k)))
-
-(defn- -entryAt
-  [#^de.kotka.lazymap.LazyMap this k]
-  (when (.containsKey this k)
-    (de.kotka.lazymap.LazyMapEntry. k (-> this .theMap (get k)))))
-
-(defn- -valAt
-  ([#^de.kotka.lazymap.LazyMap this k]
-   (.valAt this k nil))
-  ([#^de.kotka.lazymap.LazyMap this k nf]
-   (if (-> this .theMap (contains? k))
-     (-> this .theMap (get k) force)
-     nf)))
-
-; Iterable
-(defn- -iterator
-  [#^de.kotka.lazymap.LazyMap this]
-  (-> this .seq SeqIterator.))
-
-; IPersistentCollection
-(defn- -count
-  [#^de.kotka.lazymap.LazyMap this]
-  (-> this .theMap count))
-
-(defn- -seq
-  [#^de.kotka.lazymap.LazyMap this]
-  (when-let [inner-seq (-> this .theMap seq)]
-    (de.kotka.lazymap.LazyMapSeq. inner-seq)))
-
-(defn- -cons
-  [#^de.kotka.lazymap.LazyMap this o]
-  (condp instance? o
-    de.kotka.lazymap.LazyMapEntry
-                      (let [#^de.kotka.lazymap.LazyMapEntry 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 #^de.kotka.lazymap.LazyMap %1 %2) this o)))
-
-(defn- -empty
-  [#^de.kotka.lazymap.LazyMap this]
-  (-> this .theMap empty de.kotka.lazymap.LazyMap.))

File src/de/kotka/lazymap/LazyMapEntry.clj

-;-
-; 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.LazyMapEntry
-  (:gen-class
-     :init         init
-     :state        theState
-     :methods      [[getRawValue [] clojure.lang.Delay]]
-     :implements   [clojure.lang.IMapEntry]
-     :constructors {[Object Object] []}))
-
-(defstruct
-  #^{:private true}
-  entry-state
-  :theKey :theValue)
-
-(defn- -init
-  [k v]
-  [[] (struct entry-state k v)])
-
-(defn- -getKey
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (-> this .theState :theKey))
-
-(defn- -key
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (.getKey this))
-
-(defn- -getValue
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (-> this .getRawValue force))
-
-(defn- -val
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (.getValue this))
-
-(defn- -getRawValue
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (-> this .theState :theValue))
-
-(defn- -toString
-  [#^de.kotka.lazymap.LazyMapEntry this]
-  (str \[ (.key this) \space (.val this) \]))
-
-(defmethod print-method de.kotka.lazymap.LazyMapEntry
-  [#^de.kotka.lazymap.LazyMapEntry this #^java.io.Writer w]
-  (.write w (.toString this)))

File src/de/kotka/lazymap/LazyMapSeq.clj

-;-
-; 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.LazyMapSeq
-  (:gen-class
-     :init         init
-     :state        theSeq
-     :extends      clojure.lang.ASeq
-     :constructors {[clojure.lang.ISeq] []
-                    [clojure.lang.ISeq clojure.lang.IPersistentMap]
-                    [clojure.lang.IPersistentMap]}))
-
-(defn- -init
-  ([theSeq]         [[] theSeq])
-  ([theSeq theMeta] [[theMeta] theSeq]))
-
-(defn- -first
-  [#^de.kotka.lazymap.LazyMapSeq this]
-  (let [first-val (first (.theSeq this))]
-    (de.kotka.lazymap.LazyMapEntry. (key first-val) (val first-val))))
-
-(defn- -next
-  [#^de.kotka.lazymap.LazyMapSeq this]
-  (when-let [inner-rest (next (.theSeq this))]
-    (de.kotka.lazymap.LazyMapSeq. inner-rest)))

File src/main/clojure/lazymap/core.clj

+;-
+; Copyright 2008-2011 (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.
+
+(ns lazymap.core
+  "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.ILookup
+    clojure.lang.IMapEntry
+    clojure.lang.IPersistentMap
+    clojure.lang.IPersistentVector
+    clojure.lang.ASeq
+    clojure.lang.ISeq
+    clojure.lang.Seqable
+    clojure.lang.SeqIterator))
+
+(defprotocol ILazyMapEntry
+  "ILazyMapEntry describes the behaviour of a lazy MapEntry. It provides
+  an additional method (over IMapEntry), which returns the raw delay object
+  and not the forced value."
+  (get-key       [lazy-map-entry] "Return the key of the map entry.")
+  (get-raw-value [lazy-map-entry] "Return the underlying delay object."))
+
+; Extend the IMapEntry interface to act also like ILazyMapEntry.
+; For a IMapEntry get-raw-value just returns the given value as
+; wrapped in a delay. Similar a vector of two elements might be
+; used in place of a IMapEntry.
+(extend-protocol ILazyMapEntry
+  IMapEntry
+  (get-key       [#^IMapEntry this] (.getKey this))
+  (get-raw-value [#^IMapEntry this] (let [v (.getValue this)] (delay v)))
+  IPersistentVector
+  (get-key [this]
+    (when-not (= (count this) 2)
+      (throw (IllegalArgumentException.
+               "Vector used as IMapEntry must be a pair")))
+    (this 0))
+  (get-raw-value
+    [this]
+    (when-not (= (count this) 2)
+      (throw (IllegalArgumentException.
+               "Vector used as IMapEntry must be a pair")))
+    (let [v (this 1)]
+      (delay v))))
+
+(defprotocol ILazyPersistentMap
+  "ILazyPersistentMap extends IPersistentMap with a method to allow
+  transportation of the underlying delay objects."
+  (delay-assoc [lazy-map key delay] "Associates the given delay in the map."))
+
+(deftype LazyMapEntry [k v]
+  ILazyMapEntry
+  (get-key       [_] k)
+  (get-raw-value [_] v)
+  IMapEntry
+  (key           [_] k)
+  (getKey        [_] k)
+  (val           [_] (force v))
+  (getValue      [_] (force v))
+  Object
+  (toString      [_] (str \[ (pr-str k) \space (pr-str (force v)) \])))
+
+(defmethod print-method LazyMapEntry
+  [this #^java.io.Writer w]
+  (.write w (str this)))
+
+(defn create-lazy-map-seq
+  ([inner-seq]
+   (create-lazy-map-seq inner-seq nil))
+  ([inner-seq metadata]
+   (proxy [ASeq] [metadata]
+     ; ISeq
+     (first []
+       (let [first-val (first inner-seq)]
+         (LazyMapEntry. (key first-val) (val first-val))))
+     (next []
+       (when-let [inner-rest (next inner-seq)]
+         (create-lazy-map-seq inner-rest metadata)))
+     (more [] (lazy-seq (next this))))))
+
+(declare create-lazy-map)
+
+(deftype LazyPersistentMap
+  [base metadata]
+  ILazyPersistentMap
+  (delay-assoc [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 (contains? 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] (LazyMapEntry. k (base k)))
+  ; IPersistentCollection
+  (count       [this]   (count base))
+  (cons
+    [this o]
+    (if (satisfies? ILazyMapEntry o)
+      (delay-assoc this (get-key o) (get-raw-value o))
+      (into 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 create-lazy-map
+  ([base]
+   (create-lazy-map base nil))
+  ([base metadata]
+   (LazyPersistentMap. base metadata)))
+
+(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]] (delay-assoc 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)))