Invisible link to canonical for Microformats

ADR-030 Mixed Callable Declarations for a Single Symbol


Status

Accepted

Context

Slug supports function overloading through FunctionGroup dispatch by signature.

In practice, a module can define a callable name from two sources:

  • A foreign declaration, e.g. foreign trim = fn(@str str)
  • A local function value, e.g. val trim = fn(@str s, @str prefix) { ... }

Before this decision, the second declaration could fail with:

  • val 'trim' is already defined as a 'val' and cannot be reassigned

The binding immutability check ran before callable-merge behaviour, so valid overload declarations were treated as illegal reassignment. At the same time, duplicate signatures were silently overwritten in some merge paths, which made overload conflicts implicit.

Decision

Slug allows mixed callable declarations under one symbol when they contribute distinct signatures, while preserving immutable val behaviour for non-callables.

Callable Binding Rule

When a name already exists as an immutable binding:

  • If the incoming value is callable (Function, Foreign, or FunctionGroup) and the existing value is callable, merge it into the same FunctionGroup.
  • If the existing value is non-callable, keep the previous immutability error behaviour.

Duplicate Signature Rule

When merging callable declarations for the same name:

  • Reject duplicate signatures with an explicit error.
  • Do not overwrite an existing implementation for the same signature.

Non-callable Immutability Rule

No behaviour change for non-callables:

  • Reassigning or redefining an immutable val that is not part of callable overloading remains an error.

Consequences

Positive

  • Enables expected overloading across foreign and local val fn declarations.
  • Makes overload conflicts explicit by rejecting identical signatures.
  • Preserves existing immutability guarantees for non-callable values.
  • Reduces import-time failures for modules that combine foreign and local callable variants.

Negative

  • Adds additional branching and validation in environment binding logic.
  • Introduces a new explicit error path for duplicate signatures that callers may need to handle.

Neutral

  • Overload dispatch semantics remain unchanged; this decision affects binding-time behaviour only.
  • Existing code that does not mix callable declarations under one name is unaffected.