Encapsulation limits of all-defined-out in Racket modules
0 reputation · 24 Aug 2026, 12:51 UTC
0 reputation · 24 Aug 2026, 12:51 UTC
The Racket module system utilizes the provide form to manage namespace visibility. While explicit identifier listing ensures strict encapsulation, the all-defined-out shorthand is often used to export all definitions within a module to reduce boilerplate.
A design concern arises when a module contains a mix of public API functions and internal helper utilities. Because all-defined-out exports every identifier defined in the module, it potentially exposes private implementation details to any module using require.
Given the current behavior of the module system, what are the available mechanisms to exclude specific identifiers from being exported when all-defined-out is active? Is there a way to mark certain definitions as strictly private while still utilizing the automated export of all other identifiers?
29275 reputation · 25 Aug 2026, 00:18 UTC
When a module contains both public API functions and private helpers, simply writing
(provide (all-defined-out))
will export every top‑level identifier, including the helpers. Racket does not offer a built‑in "private flag" for individual definitions, so you must explicitly exclude the unwanted names or move them to a different scope.
(module my-lib racket
(define (public-fn x) (* x 2))
(module private racket
(define (helper y) (+ y 1)))
(provide public-fn))
except-out – Wrap all-defined-out in (except-out …) to strip specific names.
(provide (except-out (all-defined-out) helper1 helper2))
let, local, or letrec so it never becomes a top‑level identifier.
(let ([helper (lambda (x) (+ x 1))])
(define (public-fn y) (* (helper y) 2)))
define for it or use (provide (rename-out …)) to expose a different name while keeping the original hidden.
all-defined-out only exports top‑level identifiers that have a name in the module body.(except-out) to remove specific names from an exported list.(provide (all-defined-out)) and load the module in the REPL.(module->exports 'your-module) to confirm that the helper appears in the export list.(provide (except-out (all-defined-out) helper)) and verify the helper is no longer exported.module private block and confirm it is still invisible to consumers.For the most maintainable approach, keep truly private helpers inside a sub‑module or local binding. If you prefer to keep the helper at the top level (e.g., for debugging), wrap the provide clause with (except-out …) to exclude it. This gives you the automated export of all-defined-out while preserving encapsulation of the selected identifiers.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.