Shim java.util.Iterator: (.iterator coll)/.hasNext/.next over jolt collections (#103)
Some Clojure libraries loop with the Java Iterator protocol — e.g. hiccup's iterate! does (let [it (.iterator coll)] (while (.hasNext it) (f (.next it)))). jolt had no Iterator, so (.iterator coll) returned nil and the loop did nothing (silently dropping content). Add an (.iterator coll) object-method that returns a :jolt/iterator over any seqable, with hasNext/next; the collection is materialized via core's realize-for-iteration (late-bound through the evaluator since core loads after it, wired in api). Found while bringing up weavejester/hiccup. Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
874b6140ee
commit
288b20956c
2 changed files with 24 additions and 1 deletions
|
|
@ -14,6 +14,11 @@
|
|||
(import ./host_iface :as host)
|
||||
(import ./javatime) # java.time shims register into the evaluator at load
|
||||
|
||||
# Wire core's collection realizer into the evaluator's (.iterator coll) shim —
|
||||
# late-bound here because the evaluator loads before core. Makes Java-Iterator-
|
||||
# style loops (e.g. hiccup's iterate!) work over any jolt collection.
|
||||
(set-coll-realizer! realize-for-iteration)
|
||||
|
||||
# A defmacro expander compiles to a native fn (built as (fn args body...) and run
|
||||
# through the self-hosted pipeline) so macro expansion is COMPILED code, zero runtime
|
||||
# cost — instead of an interpreted closure, mirroring Clojure (macros are ordinary
|
||||
|
|
|
|||
|
|
@ -629,6 +629,20 @@
|
|||
# ctor fns are interned as clojure.core vars at init (install-stateful-fns!).
|
||||
(def class-ctors @{})
|
||||
(defn register-class-ctor! [nm f] (put class-ctors nm f))
|
||||
|
||||
# java.util.Iterator shim: (.iterator coll) gives a jolt iterator over any
|
||||
# seqable, with (.hasNext it) / (.next it). Some Clojure libs (e.g. hiccup's
|
||||
# iterate!) loop with the Java Iterator protocol; this makes that work over jolt
|
||||
# collections. The realizer (core/realize-for-iteration, which handles every
|
||||
# collection type) is late-bound because core loads after this file.
|
||||
(var coll-realizer nil)
|
||||
(defn set-coll-realizer! [f] (set coll-realizer f))
|
||||
(register-tagged-methods! :jolt/iterator
|
||||
@{"hasNext" (fn [self] (< (self :pos) (length (self :items))))
|
||||
"next" (fn [self]
|
||||
(def x (in (self :items) (self :pos)))
|
||||
(put self :pos (+ 1 (self :pos)))
|
||||
x)})
|
||||
# Class names evaluate to their CANONICAL NAME STRING — the same value
|
||||
# core-class returns — so (defmethod m String ...) keys match a
|
||||
# (defmulti m (comp class :body)) dispatch (ring.util.request does this).
|
||||
|
|
@ -694,7 +708,11 @@
|
|||
"getCause" (fn [e] (and (table? e) (get e :cause)))
|
||||
"toString" (fn [x] (string x))
|
||||
"equals" (fn [a b] (deep= a b))
|
||||
"hashCode" (fn [x] (hash x))})
|
||||
"hashCode" (fn [x] (hash x))
|
||||
# (.iterator coll) -> a jolt iterator (see :jolt/iterator above). Materializes
|
||||
# the collection to an indexable array via the late-bound core realizer.
|
||||
"iterator" (fn [coll] @{:jolt/type :jolt/iterator :pos 0
|
||||
:items (if coll-realizer (coll-realizer coll) @[])})})
|
||||
|
||||
(def- string-methods
|
||||
{"getBytes" (fn [s &opt charset] (buffer s))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue