Proposal: Access Control via @private Decorator in Mojo
Executive Summary
As Mojo refines its visibility model, we propose adopting an explicit decorator approach (e.g., @private, @public) rather than keyword modifiers (e.g., private def).
This approach preserves visual code skimmability, avoids grammar/parser bloat, aligns with Python’s design philosophy, and offers an incredibly simple path to deployment: initially implementing @private as a direct alias for @doc_hidden, requiring virtually zero engineering resources.
Key Arguments
1. Unmatched Consistency and Left-Margin Skimmability
In Mojo, function declarations start predictably with def or async def. This creates a clean visual anchor along the left edge when scanning a file.
Prefix modifiers break this natural rhythm:
Code-Snippet
# With keyword modifiers (Scattered left edge)
public def connect():
...
private def authenticate():
...
async def fetch_data():
...
With decorators, the core declaration keyword (def, async def) remains grounded and easy to locate at a glance:
Code-Snippet
# With @private decorator (Consistent visual anchor)
def connect():
...
@private
def authenticate():
...
async def fetch_data():
...
Because visibility is metadata about a function rather than the fundamental act of defining it, keeping it as a decorator keeps function signatures visually consistent.
2. Zero Engineering Resources: The @doc_hidden Alias Strategy
The most elegant aspect of this proposal is its path to immediate adoption. Mojo already supports @doc_hidden to suppress symbols from exported documentation and APIs.
Initially, @private can simply act as a syntactic alias or attribute wrapper for @doc_hidden:
-
No new compiler passes needed: It leverages existing attribute-handling logic right out of the box.
-
Instant utility: Developers get the ergonomic syntax they want immediately, while true scope-enforcement checks can be built under the hood later as the type checker evolves.
It turns what could be a massive language feature implementation into a zero-cost quick win.
3. Zero Parser Overhead (No Keywords or Lookahead)
Introducing private as a prefix modifier creates unnecessary parser complexity:
-
Keyword Pollution: Making
privatea reserved keyword prevents developers from using it as an identifier (e.g.,var private = True). -
Parser Lookahead: To avoid making it a strict keyword, the parser would need context-sensitive lookahead to distinguish whether
privateis a variable name or a visibility modifier precedingdeforstruct.
Because decorators already use the @ sigil, @private hooks directly into Mojo’s existing grammar with zero parser changes.
4. Preserving the Python DNA
Mojo stays true to Python by prioritizing explicit structure over keyword clutter. Python shuns C++/Java-style access keywords in favor of explicit decorators (@staticmethod, @classmethod, @property).
Using @private maintains explicit intent, fits naturally into the ecosystem, and keeps the code feeling like Mojo rather than a C family dialect.
Code Example Comparison
Recommended: Decorator Approach
Code-Snippet
struct Vault:
var balance: Float64
def __init__(out self, initial: Float64):
self.balance = initial
@private
def _reconcile(mut self):
# Internal accounting logic
pass
def deposit(mut self, amount: Float64):
self._reconcile()
self.balance += amount
Alternative: Prefix Keyword
Code-Snippet
struct Vault:
var balance: Float64
def __init__(out self, initial: Float64):
self.balance = initial
private def _reconcile(mut self):
# Disrupted indentation anchor
pass
def deposit(mut self, amount: Float64):
self._reconcile()
self.balance += amount
Conclusion
The @private decorator keeps code easy to scan, protects the grammar from keyword bloat, respects Python’s design traditions, and can be shipped almost instantly by Aliasing @doc_hidden. Adopting decorator-based visibility is the cleanest, most pragmatic choice for Mojo.