NullPointerException: Cannot invoke "Object.getClass()" because "x" is null
This document records potential improvements identified through REPL exploration and API review.
Priority: High
Problem: Missing keys produce cryptic errors:
NullPointerException: Cannot invoke "Object.getClass()" because "x" is null
Circular dependencies cause StackOverflowError with no indication of the cycle.
Examples:
(def m (fun-map {:a (fnk [missing-key] (inc missing-key))}))
(:a m) ;=> NullPointerException
(def m (fun-map {:a (fnk [b] b) :b (fnk [a] a)}))
(:a m) ;=> StackOverflowError
Suggestion:
Detect missing dependencies and throw ex-info with :missing-key and :dependent-key
Track access stack to detect and report circular dependencies with the cycle path
Priority: Low (documentation)
Problem: Only fun-maps unwrap their values. Plain maps nested inside a fun-map do not unwrap:
(def m (fun-map {:a {:b (delay 42)}})) ; {:b ...} is a plain map
(:b (:a m)) ;=> #object[clojure.lang.Delay ...] ; not 42
;; With nested fun-map, it works:
(def m (fun-map {:a (fun-map {:b (delay 42)})}))
(get-in m [:a :b]) ;=> 42
Status: Documented in concepts.adoc. This is expected behavior, not a bug.
Priority: Low (documentation)
Problem:
Iterating a fun-map (including keys, vals, seq, reduce-kv) triggers all computations. Side effects interleave with output:
(def m (fun-map {:a 1 :b (fnk [a] (println "computing") (* a 2))}))
(keys m)
;; prints: (:a computing
;; :b)
Suggestion: Document that iteration realizes all values. This is expected but may surprise users.
select-keys and into Lose Fun-Map SemanticsPriority: Low
Problem: Standard Clojure functions return plain maps:
(def m (fun-map {:a 1 :b (fnk [a] (* a 2))}))
(type (select-keys m [:a :b])) ;=> clojure.lang.PersistentArrayMap
(type (into {} m)) ;=> clojure.lang.PersistentArrayMap
Suggestion:
Document as expected behavior (values are realized during iteration). Optionally provide select-keys* that preserves wrappers for advanced use cases.
:keep-ref Interaction with fnk Is ConfusingPriority: Medium
Problem:
With :keep-ref true, fnk receives the atom itself, not the dereferenced value. But fnk auto-generates focus on the binding (the atom), not its contents.
(def state (atom [1 2 3]))
(def m (fun-map {:nums state
:count (fnk [nums] (count nums))} ; nums is atom, not vector!
:keep-ref true))
(:count m) ;=> UnsupportedOperationException: count not supported on Atom
The correct pattern requires explicit fw with @ in focus:
(fw {:keys [nums] :focus @nums}
(count @nums))
Suggestion:
Document this interaction explicitly in the API docs
Consider a :deref-focus option for fnk that focuses on @binding instead of binding
Priority: High
Problem: Several behaviors are surprising to new users:
update on a computed key replaces the fnk with the computed+updated value
Printing a fun-map with unrealized values shows [unrealized]
dissoc a dependency breaks dependent `fnk`s silently until accessed
Examples:
;; update replaces fnk
(def m (fun-map {:a 1 :b (fnk [a] (* a 2))}))
(def m2 (update m :b inc))
(:b m2) ;=> 3 ; fnk is gone, now just the value 3
;; dissoc breaks silently
(def m (fun-map {:a 1 :b (fnk [a] (* a 2))}))
(def m2 (dissoc m :a))
(:b m2) ;=> NullPointerException (only when accessed)
Suggestion: Add a "Gotchas" or "Common Pitfalls" section to README or concepts.adoc.
closeable Could Support with-openPriority: Low
Problem:
CloseableValue implements Haltable but not java.io.Closeable, so it doesn’t work with Clojure’s with-open.
(with-open [r (closeable (create-resource) #(cleanup))]
(use-resource @r))
;; Error: closeable doesn't implement Closeable
Suggestion:
Extend CloseableValue to implement java.io.Closeable in CLJ for better interop. The close method would delegate to halt!.
| ID | Description | Status |
|---|---|---|
1 | Better error messages | DONE |
2 | Document | DONE |
3 | Document iteration behavior | DONE |
4 | Document | DONE |
5 | Document | DONE |
6 | Add gotchas section | DONE |
7 |
| DONE |
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |