Status
Accepted
Context
Slug supports default parameter values in function definitions. These defaults are expressed as ordinary Slug expressions and are evaluated at function call time rather than at function definition time.
This design enables expressive defaults such as configuration lookups:
fn connect(
host = cfg("db.host", "localhost"),
port = cfg("db.port", 5432)
) { ... }
However, ambiguity arises when a default parameter expression references identifiers that are local to the function’s defining module but are not exported. Since defaults are evaluated at call time, a naïve interpretation would attempt to resolve identifiers in the caller’s lexical environment, which may not contain those bindings.
This creates a conflict between:
- lexical scoping
- module encapsulation
- expressive, non-literal default values
The language must clearly define where default parameter expressions are resolved in order to remain predictable, explicit, and consistent with Slug’s design philosophy.
Decision
Default Parameter Name Resolution
Default parameter expressions are resolved in the function’s defining module environment, not the caller’s environment.
-
Name resolution for default expressions uses:
- module-local bindings
- imports of the defining module
-
Default expressions do not see:
- caller-local bindings
- caller imports
- dynamic runtime scope
Evaluation Timing
- Default parameter expressions are evaluated at call time
- No evaluation occurs at function definition time
- No implicit closure capture is introduced
Conceptual Model
Default parameters are treated as part of the function definition, not part of the call site.
“Defaults belong to the function, not the caller.”
Example
// my/db.slug
val dbHost = cfg("db.host", "localhost")
fn connect(host = dbHost) {
...
}
// app.slug
import my.db
db.connect() // ✔ dbHost resolves within my/db module
The caller does not need access to dbHost, nor does it need to be exported.
Consequences
Positive
- Preserves module encapsulation
- Allows defaults to reference private implementation details
- Enables expressive defaults (e.g.
cfg, computed values) - Avoids hidden exports or API surface pollution
- Keeps default parameters simple and predictable
- Requires no additional runtime overhead
- Aligns with Slug’s emphasis on explicit boundaries
Negative
- Defaults cannot reference caller-local variables
- May surprise users expecting caller-scope resolution
This behavior is consistent and easily documented.
Neutral
- Default expressions behave similarly to helper functions defined within the same module
- Named parameters and pattern-based dispatch are unaffected
- No impact on runtime semantics beyond name resolution rules