Collection ops carry the receiver's metadata
conj/assoc/dissoc/disj/pop/into and empty now thread the receiver's metadata onto the result, matching Clojure (each op constructs a new collection with meta() carried forward; coll.empty() is EMPTY.withMeta(meta())). The metadata side-table is now weak so meta on intermediate collections is reclaimed with them, and empty-list-t carries an (unused) field so a metadata-bearing () is a distinct identity from the shared singleton instead of leaking meta onto every (). Unblocks metadata-driven walks (aero/integrant): (into (empty form) ...) now preserves a vector/map/set's metadata, so a postwalk whose outer fn reads (meta x) sees it.
This commit is contained in:
parent
5b55c7f7b0
commit
4ae3d3116e
7 changed files with 484 additions and 457 deletions
|
|
@ -7,7 +7,10 @@
|
|||
;;
|
||||
;; Loaded after records.ss (jrec) + collections/seq/values (the ctors it copies).
|
||||
|
||||
(define meta-table (make-eq-hashtable))
|
||||
;; Weak so a collection's metadata is reclaimed with the collection — collection
|
||||
;; ops (conj/assoc/into) carry meta forward onto fresh values, so a strong table
|
||||
;; would retain every meta-bearing intermediate.
|
||||
(define meta-table (make-weak-eq-hashtable))
|
||||
|
||||
(define (jolt-meta x)
|
||||
(cond
|
||||
|
|
@ -31,7 +34,9 @@
|
|||
((pmap? x) (make-pmap (pmap-root x) (pmap-cnt x)))
|
||||
((pset? x) (make-pset (pset-m x)))
|
||||
((jrec? x) (make-jrec (jrec-tag x) (jrec-pairs x)))
|
||||
(else x))) ; cseq / empty-list / procedure
|
||||
;; () is a shared singleton — a fresh instance keeps meta off every other ().
|
||||
((empty-list-t? x) (fresh-empty-list))
|
||||
(else x))) ; cseq / procedure
|
||||
|
||||
(define (jolt-with-meta x m)
|
||||
(cond
|
||||
|
|
@ -45,6 +50,20 @@
|
|||
(def-var! "clojure.core" "meta" jolt-meta)
|
||||
(def-var! "clojure.core" "with-meta" jolt-with-meta)
|
||||
|
||||
;; Carry SRC's collection metadata onto DST (a freshly-built collection of the
|
||||
;; same kind), as Clojure's ops do — each new collection threads its receiver's
|
||||
;; meta() forward. Returns DST. The size check is the fast path: programs that
|
||||
;; never attach collection metadata pay one O(1) check per op, no lookup.
|
||||
(define (meta-carry src dst)
|
||||
(if (fx=? 0 (hashtable-size meta-table))
|
||||
dst
|
||||
(let ((m (hashtable-ref meta-table src #f)))
|
||||
(if m
|
||||
;; never attach to the shared () singleton — use a fresh instance
|
||||
(let ((d (if (empty-list-t? dst) (fresh-empty-list) dst)))
|
||||
(hashtable-set! meta-table d m) d)
|
||||
dst))))
|
||||
|
||||
;; (type x) — Clojure's (or (:type (meta x)) (class x)). With no JVM classes the
|
||||
;; "class" is a host taxonomy: a record yields its ns-qualified class-name SYMBOL
|
||||
;; (user.TyR), everything else a keyword (:number/:vector/:seq/…).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue