Skip to content
Create Clojure maps whose values are only calculated when accessed, either from data or from java objects.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Create maps whose values are only calculated when accessed, either from data or from java objects. Supports both Clojure and Clojurescript!


The lazy-map

This macro is analogous to lazy-seq. It takes a map (instead of a seq), but the value expressions you write aren’t actually evaluated until they are accessed.

user> (def my-map
         {:cause (do (println "Getting Cause")
          :name (do (println "Getting Name")
                    "Some Name")}))

user> (:name my-map)
Getting Name
"Some Name"

user> (:name my-map)
"Some Name"

user> (:cause my-map)
Getting Cause

user> (:cause my-map)

You can also assoc new keys into a LazyMap like a regular Clojure map. If you assoc a delay it will act as a lazy value, and if you assoc anything else it acts as a regular value.

user> (def new-map (-> (assoc my-map :surname "Malabarba")
                 (assoc :delayed-surname
                        (delay (println "Resolved")
                               "Late Malabarba"))))
user> (:surname my-map)
user> (:delayed-surname my-map)
"Late Malabarba"

The to-lazy-map protocol

This protocol allows you to convert any java class into a lazy map, where each entry correponds to a method call. Since everything is lazy, you can rest assured the methods won’t actually be called until you use them.

user> (use 'lazy-map.iop)
user> (extend-lazy-map String)

user> (to-lazy-map "My Own Map!")
{:to-char-array #object[clojure.lang.Delay 0x5c3c775a {:status :pending, :val nil}],
 :empty?        #object[clojure.lang.Delay 0x774f63f2 {:status :pending, :val nil}],
 :to-string     #object[clojure.lang.Delay 0x4a62ed8c {:status :pending, :val nil}],
 :intern        #object[clojure.lang.Delay 0x4ddc7018 {:status :pending, :val nil}],
 :chars         #object[clojure.lang.Delay 0x72e5585e {:status :pending, :val nil}],
 :class         #object[clojure.lang.Delay 0x7e39e503 {:status :pending, :val nil}],
 :length        #object[clojure.lang.Delay 0x236a69c5 {:status :pending, :val nil}],
 :trim          #object[clojure.lang.Delay 0xd988100 {:status :pending, :val nil}],
 :bytes         #object[clojure.lang.Delay 0x55671f45 {:status :pending, :val nil}],
 :code-points   #object[clojure.lang.Delay 0x64c7f917 {:status :pending, :val nil}],
 :to-lower-case #object[clojure.lang.Delay 0x1493800b {:status :pending, :val nil}],
 :hash-code     #object[clojure.lang.Delay 0x5d4a8318 {:status :pending, :val nil}],
 :object        #object[clojure.lang.Delay 0x30ba32c3 {:status :pending, :val nil}],
 :to-upper-case #object[clojure.lang.Delay 0x6b6e6a82 {:status :pending, :val nil}]}

user> (:to-upper-case *1)

Note how there’s an entry for each method. Obviously, only methods that takes no arguments (0-arity) are included.

There’s also an extra :object entry holding the string itself.


Copyright © 2015 Artur Malabarba

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

You can’t perform that action at this time.