Impact of marking a var private with ^:private on cross‑namespace usage in Clojure 1.12
0 reputation · 21 Sept 2020, 08:26 UTC
0 reputation · 21 Sept 2020, 08:26 UTC
When refactoring a utility namespace in a Clojure library, a developer marks several helper functions with ^:private to hide them from the public API. The goal is to prevent accidental use of these internals while preserving the existing public functions for consumers.
However, the library is distributed as a JAR that may be AOT‑compiled by downstream projects, and some users have previously required the helpers directly via require or fully‑qualified symbols. The change raises uncertainty about whether the private metadata is respected across different Clojure versions, build tools, and class‑path scenarios, especially when the library is reloaded via tools.namespace or used in REPL‑driven development.
35775 reputation · 21 Sept 2020, 14:28 UTC
^:private is a compile-time mechanism rather than a runtime access control. It prevents the symbol from being referenced in other namespaces during compilation, but it does not protect against code that has already been compiled or uses reflection.
Marking a var with ^:private guarantees that any attempt to reference that var from another namespace will trigger a CompilerException, provided the calling code is compiled or evaluated after the var is marked private. This enforcement applies regardless of whether the calling code is AOT-compiled or loaded dynamically, as the check occurs during the compilation phase.
If a downstream project has already compiled against a public version of your library, switching to a private declaration will not cause linkage errors or silent failures at runtime immediately. Because Clojure resolves symbols to Var objects at compile time, the already-compiled bytecode holds a reference to the Var. The error will only manifest when:
There are no known breaking differences in how Clojure 1.12 versus earlier releases treats the metadata of ^:private vars regarding tools.namespace or the REPL. The behavior remains consistent:
tools.namespace/load-ns, existing compiled forms in other namespaces that reference the var will still function, but any new code evaluated referencing the private var will fail to compile.This analysis assumes standard usage patterns—direct symbol resolution (e.g., (namespace/helper)). If consumers are using @reflection or the var-get function, the ^:private metadata will have no effect, as these methods bypass the compiler's namespace visibility checks.
Diagnostic Question: Are your downstream consumers using reflection or var-get to access these helper functions?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.