From 9ac0f54e72f987a9cbc14ed7e979d3696a741554 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Thu, 11 Jun 2026 10:47:14 -0400 Subject: [PATCH] core: move promoting/unchecked arithmetic aliases, int?, num to the overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jolt numbers don't overflow, so +'/-'/*'/inc'/dec' and the whole unchecked-* family are just the checked ops — now one-line defs in core/20-coll.clj instead of ~25 seed bindings. int? and num move the same way. unchecked-divide-int now goes through quot, so dividing by zero throws like the JVM instead of silently truncating infinity. unchecked-int/long gain char handling via int, matching Clojure ((unchecked-int \a) => 97). The masking byte/short/char coercions are not aliases and stay in the seed for a later round. Also drops a second duplicate set of unchecked defns that was shadowing the first at module load. --- jolt-core/clojure/core/20-coll.clj | 34 +++++++++++++++++++++++ src/jolt/core.janet | 43 +++--------------------------- test/spec/numbers-spec.janet | 35 ++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 39 deletions(-) diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index a497819..293f937 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -848,3 +848,37 @@ ;; there, so vector? can't separate them and lists read as ifn?. (defn ifn? [x] (or (fn? x) (keyword? x) (symbol? x) (map? x) (set? x) (vector? x) (var? x))) + +;; Auto-promoting (') and unchecked arithmetic. Jolt numbers don't overflow, +;; so all of these are the checked ops; fixed arities mirror Clojure's +;; signatures. unchecked-divide-int goes through quot, so dividing by zero +;; throws as on the JVM (the old seed fn silently truncated infinity). +(def +' +) +(def -' -) +(def *' *) +(def inc' inc) +(def dec' dec) +(defn unchecked-add [x y] (+ x y)) +(defn unchecked-subtract [x y] (- x y)) +(defn unchecked-multiply [x y] (* x y)) +(defn unchecked-negate [x] (- x)) +(defn unchecked-inc [x] (+ x 1)) +(defn unchecked-dec [x] (- x 1)) +(def unchecked-add-int unchecked-add) +(def unchecked-subtract-int unchecked-subtract) +(def unchecked-multiply-int unchecked-multiply) +(def unchecked-negate-int unchecked-negate) +(def unchecked-inc-int unchecked-inc) +(def unchecked-dec-int unchecked-dec) +(defn unchecked-divide-int [x y] (quot x y)) +(defn unchecked-remainder-int [x y] (rem x y)) +(defn unchecked-int [x] (int x)) +(def unchecked-long unchecked-int) + +;; int? is integer? on jolt: one number type, so fixed-precision and +;; arbitrary-precision integers coincide. +(defn int? [x] (integer? x)) + +;; num: Clojure coerces to java.lang.Number; jolt just checks. +(defn num [x] + (if (number? x) x (throw (str "num requires a number, got: " x)))) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index ad653cb..0e4ccbf 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2020,7 +2020,8 @@ (def core-long (fn [x] (if (core-char? x) (x :ch) (math/trunc x)))) (def core-double (fn [x] (* 1.0 (if (core-char? x) (x :ch) x)))) (def core-float core-double) -(def core-num (fn [x] (if (number? x) x (error (string "num requires a number, got " (type x)))))) +# num and the unchecked-*/promoting-' arithmetic live in the Clojure +# collection tier (core/20-coll.clj) — jolt numbers don't overflow. (defn core-char [x] "(char code-or-char) -> a character value." (cond @@ -2028,10 +2029,6 @@ (number? x) (make-char (math/trunc x)) (string? x) (make-char (in x 0)) (error "char expects a number or character"))) -(def core-unchecked-inc (fn [x] (+ x 1))) -(def core-unchecked-dec (fn [x] (- x 1))) -(def core-unchecked-add (fn [& xs] (+ ;xs))) -(def core-unchecked-subtract (fn [& xs] (- ;xs))) # ============================================================ # Hash @@ -2626,15 +2623,8 @@ (error "persistent! requires a transient"))) # Unchecked arithmetic — Jolt numbers don't overflow, so these are plain ops. -(defn core-unchecked-add [a b] (+ a b)) -(defn core-unchecked-subtract [a b] (- a b)) -(defn core-unchecked-multiply [a b] (* a b)) -(defn core-unchecked-negate [a] (- a)) -(defn core-unchecked-inc [a] (+ a 1)) -(defn core-unchecked-dec [a] (- a 1)) -(defn core-unchecked-divide-int [a b] (math/trunc (/ a b))) -(defn core-unchecked-remainder-int [a b] (% a b)) -(defn core-unchecked-int [a] (math/trunc a)) +# unchecked-* arithmetic lives in the Clojure collection tier +# (core/20-coll.clj); only the masking byte/short/char coercions remain above. # Hashing helpers # Hashes are masked to 24 bits at each step so intermediate products stay within @@ -2700,13 +2690,6 @@ "/" core-/ "inc" core-inc "dec" core-dec - # auto-promoting variants — Jolt numbers don't overflow, so these are the - # same as their non-quoted counterparts - "+'" core-+ - "-'" core-sub - "*'" core-* - "inc'" core-inc - "dec'" core-dec "mod" core-mod "rem" core-rem "quot" core-quot @@ -2761,27 +2744,10 @@ "assoc!" core-assoc! "dissoc!" core-dissoc! "pop!" core-pop! - "unchecked-add" core-unchecked-add - "unchecked-add-int" core-unchecked-add - "unchecked-subtract" core-unchecked-subtract - "unchecked-subtract-int" core-unchecked-subtract - "unchecked-multiply" core-unchecked-multiply - "unchecked-multiply-int" core-unchecked-multiply - "unchecked-negate" core-unchecked-negate - "unchecked-negate-int" core-unchecked-negate - "unchecked-inc" core-unchecked-inc - "unchecked-inc-int" core-unchecked-inc - "unchecked-dec" core-unchecked-dec - "unchecked-dec-int" core-unchecked-dec - "unchecked-divide-int" core-unchecked-divide-int - "unchecked-remainder-int" core-unchecked-remainder-int - "unchecked-int" core-unchecked-int - "unchecked-long" core-unchecked-int "hash-combine" core-hash-combine "hash-ordered-coll" core-hash-ordered-coll "hash-unordered-coll" core-hash-unordered-coll "gensym" gensym - "int?" core-integer? "compare" core-compare "type" core-type "slurp" core-slurp @@ -2946,7 +2912,6 @@ "long" core-long "double" core-double "float" core-float - "num" core-num "char" core-char "char?" core-char? # Hash diff --git a/test/spec/numbers-spec.janet b/test/spec/numbers-spec.janet index 016c0a2..38cb1b9 100644 --- a/test/spec/numbers-spec.janet +++ b/test/spec/numbers-spec.janet @@ -157,3 +157,38 @@ ["parse-boolean case" "nil" "(parse-boolean \"True\")"] ["parse-boolean junk" "nil" "(parse-boolean \"yes\")"] ["parse-boolean throws" :throws "(parse-boolean true)"]) + +# Jolt numbers don't overflow, so the auto-promoting (') and unchecked +# variants are aliases of the checked ops (overlay defs, core/20-coll.clj). +(defspec "numbers / promoting & unchecked aliases" + ["+'" "3" "(+' 1 2)"] + ["-'" "3" "(-' 5 2)"] + ["*'" "12" "(*' 3 4)"] + ["inc'" "6" "(inc' 5)"] + ["dec'" "4" "(dec' 5)"] + ["unchecked-add" "5" "(unchecked-add 2 3)"] + ["unchecked-add-int" "5" "(unchecked-add-int 2 3)"] + ["unchecked-subtract" "3" "(unchecked-subtract 5 2)"] + ["unchecked-subtract-int" "3" "(unchecked-subtract-int 5 2)"] + ["unchecked-multiply" "12" "(unchecked-multiply 3 4)"] + ["unchecked-multiply-int" "12" "(unchecked-multiply-int 3 4)"] + ["unchecked-negate" "-5" "(unchecked-negate 5)"] + ["unchecked-negate-int" "-5" "(unchecked-negate-int 5)"] + ["unchecked-inc" "2" "(unchecked-inc 1)"] + ["unchecked-inc-int" "2" "(unchecked-inc-int 1)"] + ["unchecked-dec" "0" "(unchecked-dec 1)"] + ["unchecked-dec-int" "0" "(unchecked-dec-int 1)"] + ["unchecked-divide-int" "3" "(unchecked-divide-int 7 2)"] + ["unchecked-divide-int negative truncates toward zero" "-3" "(unchecked-divide-int -7 2)"] + ["unchecked-divide-int by zero throws" :throws "(unchecked-divide-int 1 0)"] + ["unchecked-remainder-int" "1" "(unchecked-remainder-int 7 2)"] + ["unchecked-remainder-int negative" "-1" "(unchecked-remainder-int -7 2)"] + ["unchecked-int truncates" "3" "(unchecked-int 3.7)"] + ["unchecked-int negative" "-3" "(unchecked-int -3.7)"] + ["unchecked-long" "3" "(unchecked-long 3.7)"] + ["int? on integer" "true" "(int? 5)"] + ["int? on double" "false" "(int? 5.5)"] + ["int? on non-number" "false" "(int? \"5\")"] + ["num passes a number through" "5" "(num 5)"] + ["num on a double" "5.5" "(num 5.5)"] + ["num throws on non-number" :throws "(num \"x\")"])