You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Clojure's numeric tower is useful, but it can put a lot of steps between you and simple arithmetic. Unfortunately, while Clojure will warn you when reflection is required to invoke a function, it will not warn you when reflection is required to perform math. The only reliable way to discover whether you're calling clojure.lang.Number.add(Object, Object) or clojure.lang.Number.add(long, long) is to use a profiler or decompiler.
Or, you can just bypass Clojure's math operators altogether.
In the clj-commons.primitive-math namespace, there are equivalents for every arithmetic operator and comparator that will give a reflection warning if it cannot compile down to a simple, predictable, unboxed mathematical operation.
clj-commons.primitive-math> (set! *warn-on-reflection* true)
true
clj-commons.primitive-math> (+33)
6
clj-commons.primitive-math> (defnadder [x] (+1 x))
;; gives a reflection warning
clj-commons.primitive-math> (defnadder [^long x] (+1 x))
;; no reflection warning
clj-commons.primitive-math> (+3.03)
;; gives a reflection warning AND throws an exception
To support operations on both long and double types without any reflection, these operators are defined as macros. This means they cannot be used as higher-order functions:
clj-commons.primitive-math> (apply + [123])
;; throws a 'cannot take value of macro' exception
In practice, it's usually preferable to import the namespace with a prefix, and use p/+ and p/== operators alongside the normal Clojure functions. However, if in a particular namespace you never need to use higher-order operators, you can call (primitive-math/use-primitive-operators) to swap out the Clojure operators for their primitive equivalents. This can be reversed, using (primitive-math/unuse-primitive-operators).
Notes
Pre-1.0.0 versions of primitive-math used a single-segment namespace. This causes problems for Graal and clj-easy. For 1.0.0, everything was copied under the clj-commons namespace. The code is effectively identical, however, so unless you are using Graal, you don't need to make any changes. If you are using Graal, make sure you only require the clj-commons.* namespaces to avoid issues.