-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path10_introduction.html
More file actions
125 lines (125 loc) · 9.05 KB
/
Copy path10_introduction.html
File metadata and controls
125 lines (125 loc) · 9.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>Introduction</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script><link rel="stylesheet" type="text/css" href="css/randomseed.css" /></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Lazy map</span> <span class="project-version">1.0.4</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="10_introduction.html"><div class="inner"><span>Introduction</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1 "><a href="io.randomseed.lazy-map.html"><div class="inner"><span>io.randomseed.lazy-map</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction" id="introduction"></a>Introduction</h1>
<blockquote>
<p>Lazy maps for Clojure</p>
</blockquote>
<p><a href="https://clojars.org/io.randomseed/lazy-map"><img src="https://img.shields.io/clojars/v/io.randomseed/lazy-map.svg" alt="lazy-map on Clojars" /></a> <a href="https://cljdoc.org/d/io.randomseed/lazy-map/CURRENT"><img src="https://cljdoc.org/badge/io.randomseed/lazy-map" alt="lazy-map on cljdoc" /></a> <a href="https://dl.circleci.com/status-badge/redirect/gh/randomseed-io/lazy-map/tree/master"><img src="https://dl.circleci.com/status-badge/img/gh/randomseed-io/lazy-map/tree/master.svg?style=svg" alt="CircleCI" /></a></p>
<h2><a href="#summary" id="summary"></a>Summary</h2>
<p>This library provides a new Clojure data type: the <em>lazy map</em>. Lazy maps behave like regular (persistent) maps, except their values are not computed until they are actually requested.</p>
<p>It is based on code from <a href="https://github.com/raxod502/lazy-map">raxod502</a>, with three important changes:</p>
<ul>
<li>
<p>The equality method is modified to <strong>compare only to maps having the same keys</strong>. This prevents unwanted realization of values when a lazy map is compared with booleans, keywords, or other non-map-like objects, which will always differ from a map anyway so there is no reason to force values.</p>
</li>
<li>
<p><strong>The namespace is <code>io.randomseed.lazy-map</code></strong>, and the artifact is <code>io.randomseed/lazy-map</code>, to prevent collisions (many lazy map packages are published as <code>lazy-map/lazy-map</code>).</p>
</li>
<li>
<p>The JAR includes <strong>AOT-compiled Java classes</strong> so the types are available to consumers.</p>
</li>
<li>
<p>Additional performance improvements and fixes.</p>
</li>
</ul>
<h2><a href="#installation" id="installation"></a>Installation</h2>
<p>To use <code>lazy-map</code> in your project, add the following to the dependencies section of <code>project.clj</code> or <code>build.boot</code>:</p>
<pre><code class="language-clojure">[io.randomseed/lazy-map "1.0.4"]
</code></pre>
<p>For <code>deps.edn</code>, add the following under the <code>:deps</code> or <code>:extra-deps</code> key:</p>
<pre><code class="language-clojure">io.randomseed/lazy-map {:mvn/version "1.0.4"}
</code></pre>
<p>Additionally, if you want to use the specs and generators provided by <code>lazy-map</code>, you can add (in your development profile):</p>
<pre><code class="language-clojure">org.clojure/spec.alpha {:mvn/version "0.6.249"}
org.clojure/test.check {:mvn/version "1.1.3"}
</code></pre>
<p>You can also download the JAR from <a href="https://clojars.org/io.randomseed/lazy-map">Clojars</a>.</p>
<h2><a href="#usage" id="usage"></a>Usage</h2>
<p>Start by requiring the namespace:</p>
<pre><code>user> (require '[io.randomseed.lazy-map :as lm])
</code></pre>
<p>You can then construct a lazy map using the <code>lazy-map</code> macro.</p>
<pre><code>user> (def m (lm/lazy-map {:a (do (println "resolved :a") "value :a")
:b (do (println "resolved :b") "value :b")}))
#'user/m
user> m
{:a <unrealized>, :b <unrealized>}
</code></pre>
<p>When you request a value from the map, it will be evaluated and its value will be cached:</p>
<pre><code>user> (:a m)
resolved :a
"value :a"
user> (:a m)
"value :a"
</code></pre>
<p>You can <code>assoc</code> values onto lazy maps just like regular maps. If you <code>assoc</code> a delay, it will be treated as an unrealized value and not forced until necessary:</p>
<pre><code>user> (assoc (lm/lazy-map {}) :a 1 :b (delay 2))
{:a 1, :b <unrealized>}
</code></pre>
<p>Lazy maps are very lazy. In practice, this means they probably will not compute their values until absolutely necessary. For example, taking the <code>seq</code> of a lazy map does not force any computation, and map entries have been made lazy as well:</p>
<pre><code>user> (def m (lm/lazy-map {:a (do (println "resolved :a") "value :a")
:b (do (println "resolved :b") "value :b")}))
#'io.randomseed.io.randomseed.lazy-map/m
io.randomseed.lazy-map> (dorun m)
nil
io.randomseed.lazy-map> (keys m)
(:a :b)
io.randomseed.lazy-map> (key (first m))
:a
io.randomseed.lazy-map> (val (first m))
resolved :a
"value :a"
</code></pre>
<p>You can also initialize a lazy map from a regular map, where delays are taken as unrealized values:</p>
<pre><code>user> (lm/->LazyMap {:a 1 :b (delay 2)})
{:a 1, :b <unrealized>}
</code></pre>
<p>You might prefer to use <code>->?LazyMap</code> instead of <code>->LazyMap</code>. The only difference is that <code>->?LazyMap</code> acts as the identity function if you pass it a map that is already lazy. This prevents nested lazy maps, which are not inherently wrong but which could be bad for performance if you nest them thousands of layers deep.</p>
<p>There are also some utility functions for dealing with lazy maps. You can use <code>force-map</code> to compute all of the values in a lazy map. Alternatively, you can use <code>freeze-map</code> to replace all the unrealized values with a placeholder. Here is an illustration:</p>
<pre><code>user> (lm/force-map
(lm/->LazyMap {:a (delay :foo)
:b :bar}))
{:a :foo, :b :bar}
user> (lm/force-map
(lm/freeze-map
:quux
(lm/->LazyMap {:a (delay :foo)
:b :bar})))
{:a :quux, :b :bar}
</code></pre>
<p>Finally, lazy maps will automatically avoid computing their values when they are converted to strings using <code>str</code>, <code>pr-str</code>, and <code>print-dup</code>. To accomplish the same for <code>pprint</code>, you must use a special pretty-print dispatch function:</p>
<pre><code>user> (pp/with-pprint-dispatch lm/lazy-map-dispatch
(pp/pprint (lm/lazy-map {:a (println "lazy")})))
{:a <unrealized>}
</code></pre>
<p>Check out the <a href="test/io/randomseed/lazy_map_test.clj">unit tests</a> for more information on the exact behavior of lazy maps.</p>
<h2><a href="#organization" id="organization"></a>Organization</h2>
<p>All the code is currently in the <code>io.randomseed.lazy-map</code> namespace, and the unit tests are in the <code>io.randomseed.lazy-map-test</code> namespace.</p>
<h2><a href="#see-also" id="see-also"></a>See also</h2>
<p><strong><a href="https://github.com/Malabarba/lazy-map-clojure">Malabarba’s implementation</a> of lazy maps in Clojure.</strong></p>
<p>Features unique to <code>malabarba/lazy-map</code>:</p>
<ul>
<li>ClojureScript support</li>
<li>Transform Java classes into lazy maps (methods become keys)</li>
</ul>
<p>Features unique to <code>raxod502/lazy-map</code>:</p>
<ul>
<li>More robust handling of laziness: all possible operations on maps are supported correctly (e.g. <code>seq</code> and <code>reduce-kv</code>)</li>
<li>Pretty string representation and support for pretty-printing</li>
</ul>
<p>Features unique to <code>io.randomseed/lazy-map</code>:</p>
<ul>
<li>Equality method compares only to maps (so no sentinel will cause accidental realization of values). Lazy map is realized only when the following criteria are met:
<ul>
<li>given object is not the same object as ours,</li>
<li>given object is also a map,</li>
<li>given object has the same (non-zero) count as ours,</li>
<li>given object has the same keys as ours.</li>
</ul>
</li>
<li>Artifact group is unique (no name collisions with packages requiring other lazy map libraries).</li>
<li>Fixed implementation of <code>clojure.lang.IPersistentCollection/empty</code>.</li>
<li>Simplified implementation of <code>clojure.lang.IMapIterable/keyIterator</code>.</li>
<li>AOT-compiled Java classes.</li>
</ul>
</div></div></div></body></html>