RFD-0011 — Two-tier visibility and direct-dependency rule
Question
How fine-grained should Argon’s visibility system be, and what’s the rule for using items from packages that aren’t direct dependencies?
Decision
Two tiers of visibility. A declaration is either pub (package-public) or module-internal (the default). No pub(crate), no pub(super), no fine-grained access modifiers (yet). I (@ivan_sharpe) do have plans to introduce pub(pkg) and pub(mod) and pub(super), but these are not a priority at the moment until a need arises.
Direct-dependency rule. A package may use an item only if that item’s owning package is a direct dependency in the consuming package’s manifest. Items reachable through transitive dependencies are not usable without an explicit declaration.
Rationale
Two tiers cover the cases worth distinguishing. The case for fine-grained visibility (“only this neighboring module can see this”) almost never holds in practice; the situations that motivate it are usually solved better by reorganizing the module structure. Two tiers (visible at package boundary, visible only inside this module) match the actual modeling decisions ontology authors make.
Direct-dependency rule prevents accidental coupling. A package that uses items from a transitive dependency creates an implicit dependency that breaks when the intermediate package’s authors restructure their re-exports. Forcing every used package to appear in the consuming manifest makes the dependency graph explicit and the consequences of upstream changes predictable.
Re-exports are how transitive items become usable. A package that wants to expose items from its dependencies for downstream consumption uses pub use. The downstream consumer then declares the re-exporting package as a direct dependency, not the original one. This is how UFO-prelude-style patterns work: pub use ufo::prelude::* in a package’s prelude.ar creates a controlled re-export surface.
Consequences
- Manifest’s
[dependencies]table is the authoritative list of packages this package may import from. - The compiler rejects imports from packages not listed as direct dependencies, even when the symbol is reachable through transitive re-exports without a re-export declaration.
pub use foo::bar::*is the canonical re-export pattern.- No language-level access modifier between
puband module-internal. If a finer split is needed, the right answer is usually a module reorganization.