instance? recognizes common host interfaces

(instance? clojure.lang.Named :a), java.lang.CharSequence/Number,
java.util.Map/Set/List/Collection, clojure.lang.Associative now report
true for jolt's value model, matching the JVM (a Map is not a Collection;
a List excludes sets/maps). Libraries branch on these — data.json's
default-write-key-fn keys on clojure.lang.Named, so map keys serialized
with the leading colon before this.
This commit is contained in:
Yogthos 2026-06-24 14:21:59 -04:00
parent 21895cb932
commit c6b8f31608
2 changed files with 28 additions and 0 deletions

View file

@ -535,6 +535,24 @@
((string=? iface "IFn")
(or (procedure? val) (keyword? val) (symbol-t? val)
(pmap? val) (pset? val) (pvec? val)))
;; host-class interfaces libraries branch on (data.json, etc.).
;; Matched by last segment, so java.util.Map and Map both hit.
((string=? iface "Named") (or (keyword? val) (symbol-t? val)))
((string=? iface "CharSequence") (string? val))
((string=? iface "Number") (number? val))
((string=? iface "Map") (or (pmap? val) (htable-sorted-map? val)))
((string=? iface "Set") (or (pset? val) (htable-sorted-set? val)))
;; a Java List is a vector or a seq/list — not a set or map.
((string=? iface "List")
(or (and (pvec? val) (not (jolt-map-entry? val)))
(cseq? val) (empty-list-t? val) (jolt-lazyseq? val)))
;; a Java Collection is any of those plus a set — but NOT a map.
((string=? iface "Collection")
(or (pvec? val) (pset? val) (cseq? val) (empty-list-t? val)
(jolt-lazyseq? val) (htable-sorted-set? val)))
((string=? iface "Associative")
(or (pmap? val) (htable-sorted-map? val)
(and (pvec? val) (not (jolt-map-entry? val)))))
(else 'none))))
(if (eq? hit 'none) 'pass (if hit #t #f))))))

View file

@ -2971,4 +2971,14 @@
{:suite "protocols / extend on host classes" :label "dispatch by java.util.Map/Collection/CharSequence" :expected "[:map :coll :str :obj]" :actual "(do (defprotocol W (-w [x])) (extend java.util.Map W {:-w (fn [_] :map)}) (extend java.util.Collection W {:-w (fn [_] :coll)}) (extend java.lang.CharSequence W {:-w (fn [_] :str)}) (extend java.lang.Object W {:-w (fn [_] :obj)}) [(-w {:a 1}) (-w [1 2]) (-w \"s\") (-w 42)])"}
{:suite "protocols / extend on host classes" :label "satisfies? via java.util.Map" :expected "true" :actual "(do (defprotocol Q (qq [x])) (extend java.util.Map Q {:qq (fn [_] :m)}) (satisfies? Q {}))"}
{:suite "protocols / extend on host classes" :label "extends? on a qualified host class" :expected "true" :actual "(do (defprotocol Qe (qe [x])) (extend java.util.Collection Qe {:qe (fn [_] :c)}) (extends? Qe java.util.Collection))"}
{:suite "interop / instance? on host interfaces" :label "keyword is Named" :expected "true" :actual "(instance? clojure.lang.Named :a)"}
{:suite "interop / instance? on host interfaces" :label "string is CharSequence" :expected "true" :actual "(instance? java.lang.CharSequence \"s\")"}
{:suite "interop / instance? on host interfaces" :label "number is Number" :expected "true" :actual "(instance? java.lang.Number 42)"}
{:suite "interop / instance? on host interfaces" :label "map is java.util.Map" :expected "true" :actual "(instance? java.util.Map {:a 1})"}
{:suite "interop / instance? on host interfaces" :label "vector is java.util.List" :expected "true" :actual "(instance? java.util.List [1 2])"}
{:suite "interop / instance? on host interfaces" :label "list is java.util.Collection" :expected "true" :actual "(instance? java.util.Collection (list 1))"}
{:suite "interop / instance? on host interfaces" :label "set is java.util.Set" :expected "true" :actual "(instance? java.util.Set #{1})"}
{:suite "interop / instance? on host interfaces" :label "map is not a Collection" :expected "false" :actual "(instance? java.util.Collection {:a 1})"}
{:suite "interop / instance? on host interfaces" :label "vector is Associative" :expected "true" :actual "(instance? clojure.lang.Associative [1])"}
{:suite "interop / instance? on host interfaces" :label "string is not Number" :expected "false" :actual "(instance? java.lang.Number \"s\")"}
]