Learn when primitive math and type hints actually help Clojure performance, and how to use them surgically in hot paths instead of turning the whole codebase into pseudo-Java.
Boxing: Representing a primitive value such as
longordoubleas a heap-allocated object so it can participate in generic object-oriented operations.
Primitives and type hints matter in Clojure because the JVM distinguishes between:
long and doubleLong and DoubleThat distinction can affect both speed and allocation in hot numeric paths. But the right lesson is not “annotate everything.” The right lesson is “use explicit numeric intent where measurement shows the runtime cost matters.”
1(defn mean-array
2 ^double [^doubles xs]
3 (loop [i 0
4 total (double 0.0)]
5 (if (< i (alength xs))
6 (recur (unchecked-inc-int i)
7 (+ total (aget xs i)))
8 (/ total (double (alength xs))))))
This style is worth considering only because the function is explicitly array-heavy and numeric. For ordinary business logic over maps and sequences, it is often unnecessary.
Hints are a performance tool, not a style requirement. Overuse creates two problems:
That is why the best pattern is to keep primitive-heavy code in small, reviewable hot-path helpers instead of spreading hints across the entire codebase.
Useful places to be explicit include:
^longs and ^doublesWhen Clojure code uses generic numeric operations on boxed values inside tight loops, the cost may show up as:
Again, this matters most in hot paths, parsers, counters, statistics, simulations, and dense data processing.
It matters much less in ordinary application code built around maps, keywords, validation, routing, and boundary orchestration.
When the goal is interop performance, *warn-on-reflection* helps reveal where the compiler still lacks enough type information. That makes hints more precise because you can target the actual uncertainty rather than guessing.
Hints are most valuable when they clarify:
The point is not to silence every warning. It is to remove the costly ones in paths that are actually hot.
Primitive-friendly code sometimes also uses unchecked arithmetic helpers such as unchecked-inc-int or unchecked-add. Those can reduce overhead in tight loops, but they also remove overflow safety.
That means you should ask two questions separately:
Those are related, but they are not the same choice.
If a path is slow because:
then adding hints will not solve the real problem.
That increases code noise faster than it increases performance.
Algorithm and data shape still dominate.
Hints help most when they eliminate real reflective dispatch in hot code.
The readability cost is real and should be earned.
Use primitives and type hints where the path is proven hot, numeric, or reflection-heavy. Keep those low-level helpers narrow and explicit, use reflection warnings to place hints precisely, and treat unchecked arithmetic as a separate trade-off that must be justified. Let ordinary application code stay idiomatic unless performance evidence says otherwise. In Clojure, hints are surgical tools, not a blanket coding style.