type-aware record equality + assoc extension + record-shape test (jolt-t34 R3)
Record shape-recs now compare type-aware like the table form: eq-map-pairs
returns nil for a record so equality falls to deep=, which is type-aware via the
per-type interned descriptor. A record equals only a same-type record, never a
plain map (cross-type and record-vs-map were wrongly equal under JOLT_SHAPE).
assoc of a new (undeclared) key keeps the record type and grows a slot, matching
Clojure's record-extension semantics.
Adds test/integration/record-shape-test.janet locking in the runtime record
mechanism (tag, field access, virtual :jolt/deftype, type-preserving assoc,
dissoc demotion, type-aware equality, #ns.Type{...} printing).
This commit is contained in:
parent
2900d36176
commit
7900173d45
2 changed files with 69 additions and 1 deletions
64
test/integration/record-shape-test.janet
Normal file
64
test/integration/record-shape-test.janet
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Records as shape-recs (jolt-t34 R3). A user record (defrecord/deftype) under
|
||||
# JOLT_SHAPE is a shape-rec whose descriptor ALSO carries :type (the type tag),
|
||||
# laid out in DECLARED field order. These build records directly via the runtime
|
||||
# (make-record) and assert that every map/record operation treats them the way
|
||||
# Clojure does — and crucially that type identity is preserved (a record is not
|
||||
# a plain map, and two records are equal only when their types match).
|
||||
(use ../../src/jolt/types)
|
||||
(use ../../src/jolt/core)
|
||||
|
||||
(var fails 0)
|
||||
(defn check [label got want]
|
||||
(if (deep= got want) (print " ok " label)
|
||||
(do (++ fails) (printf " FAIL %s: got %j want %j" label got want))))
|
||||
|
||||
# (defrecord Point [x y]) instance (->Point 1 2)
|
||||
(def P (make-record "my.Point" [:x :y] [1 2]))
|
||||
|
||||
# --- it IS a shape-rec, and reports its type ---------------------------------
|
||||
(check "shape-rec?" (shape-rec? P) true)
|
||||
(check "record-tag" (record-tag P) "my.Point")
|
||||
(check "map?" (core-map? P) true)
|
||||
|
||||
# --- field access in declared order ------------------------------------------
|
||||
(check "get x" (core-get P :x nil) 1)
|
||||
(check "get y" (core-get P :y nil) 2)
|
||||
(check "get miss default" (core-get P :z :d) :d)
|
||||
(check "count" (core-count P) 2)
|
||||
(check "contains?" (core-contains? P :x) true)
|
||||
|
||||
# --- the virtual :jolt/deftype key keeps every (get obj :jolt/deftype) site
|
||||
# (record?/dispatch) working without special-casing each one ------------------
|
||||
(check "virtual deftype" (core-get P :jolt/deftype nil) "my.Point")
|
||||
|
||||
# --- assoc preserves the type: in place for a declared field, and grows a
|
||||
# slot Clojure-style for a new key (the result is still a record) -------------
|
||||
(def P2 (core-assoc P :x 9))
|
||||
(check "assoc field tag" (record-tag P2) "my.Point")
|
||||
(check "assoc field val" (core-get P2 :x nil) 9)
|
||||
(check "assoc keeps other" (core-get P2 :y nil) 2)
|
||||
(def P3 (core-assoc P :z 3))
|
||||
(check "assoc new tag" (record-tag P3) "my.Point")
|
||||
(check "assoc new val" (core-get P3 :z nil) 3)
|
||||
(check "assoc new keeps" (core-get P3 :x nil) 1)
|
||||
|
||||
# --- dissoc of a declared field demotes to a plain map (Clojure semantics) ----
|
||||
(def D (core-dissoc P :x))
|
||||
(check "dissoc demotes" (record-tag D) nil)
|
||||
(check "dissoc gone" (core-get D :x :gone) :gone)
|
||||
(check "dissoc keeps" (core-get D :y nil) 2)
|
||||
|
||||
# --- equality is TYPE-AWARE: same type + same fields equal; a different type
|
||||
# or a plain map with the same fields is NOT equal ----------------------------
|
||||
(check "= same type" (jolt-equal? P (make-record "my.Point" [:x :y] [1 2])) true)
|
||||
(check "not= diff field" (jolt-equal? P (make-record "my.Point" [:x :y] [1 9])) false)
|
||||
(check "not= diff type" (jolt-equal? P (make-record "my.Other" [:x :y] [1 2])) false)
|
||||
(check "not= record vs map" (jolt-equal? P {:x 1 :y 2}) false)
|
||||
(check "not= map vs record" (jolt-equal? {:x 1 :y 2} P) false)
|
||||
|
||||
# --- printing: Clojure record syntax #ns.Type{:k v, ...}, fields in order -----
|
||||
(check "pr record" (core-pr-str1 P) "#my.Point{:x 1, :y 2}")
|
||||
|
||||
(if (> fails 0)
|
||||
(error (string "record-shape: " fails " failing check(s)"))
|
||||
(print "\nRecord shape passed!"))
|
||||
Loading…
Add table
Add a link
Reference in a new issue