Namespaces: namespace Foo { ... } block with export/internal visibility, not per-function @Namespace #76

Closed
opened 2026-09-02 04:52:38 +02:00 by orkun · 1 comment
Owner

Today every exported function is annotated one-by-one, e.g. @Namespace(Combat) function combat_amount() -> int { ... }, scattering a namespace across many annotations and exporting each individually.

Proposal: a block form that declares the namespace once and controls visibility declaratively, e.g.

namespace Combat {
  export function amount() -> int { ... }
  internal function pending() -> int { ... }
}

Improves readability and makes a package's public surface obvious. Seen throughout packages/ludic.gameplay and friends.

Today every exported function is annotated one-by-one, e.g. `@Namespace(Combat) function combat_amount() -> int { ... }`, scattering a namespace across many annotations and exporting each individually. Proposal: a block form that declares the namespace once and controls visibility declaratively, e.g. ``` namespace Combat { export function amount() -> int { ... } internal function pending() -> int { ... } } ``` Improves readability and makes a package's public surface obvious. Seen throughout packages/ludic.gameplay and friends.
Author
Owner

Shipped in 23e232e (full suite 116/0, goldens byte-identical, fixpoint intact).

namespace Name { ... } declares a Name.* namespace once and controls its public surface declaratively, instead of the per-function @Namespace(Name):

namespace Combat {
  export function amount() -> int { return base() + 5 }
  internal function base() -> int { return 10 }
  export function double(n: int) -> int { return n + n }
}

Inside the block each function short(…) is emitted as namelower_short; export (the default) makes it callable from anywhere as Name.short(…), and internal keeps it a private helper — emitted, and callable by its short name from siblings in the block (sibling calls are rewritten to the emitted name), but not part of the Name.* surface: calling Combat.base() is a compile error (Combat.base is internal to namespace Combat). So a package's public API reads at a glance, which is what packages/ludic.gameplay & friends wanted.

It is the block sugar for the per-function @Namespace annotation and goes through the same dispatch path, so a namespace declared the old way is unchanged (the ludic.gameplay controller tests still pass). Verified by examples/library/namespace_block.ludic — exported methods dispatch (15 42 100), an internal sibling is reachable from inside, and Combat.base() from outside is a compile error. namespace is registered across the vocabulary (ludic_syntax.h + JetBrains + TextMate + docs page + inventory) so the vocab/impl/docs checks stay green.

Shipped in 23e232e (full suite 116/0, goldens byte-identical, fixpoint intact). **`namespace Name { ... }`** declares a `Name.*` namespace once and controls its public surface declaratively, instead of the per-function `@Namespace(Name)`: namespace Combat { export function amount() -> int { return base() + 5 } internal function base() -> int { return 10 } export function double(n: int) -> int { return n + n } } Inside the block each `function short(…)` is emitted as `namelower_short`; **export** (the default) makes it callable from anywhere as `Name.short(…)`, and **internal** keeps it a private helper — emitted, and callable by its short name from siblings in the block (sibling calls are rewritten to the emitted name), but **not** part of the `Name.*` surface: calling `Combat.base()` is a compile error (`Combat.base is internal to namespace Combat`). So a package's public API reads at a glance, which is what packages/ludic.gameplay & friends wanted. It is the block sugar for the per-function `@Namespace` annotation and goes through the same dispatch path, so a namespace declared the old way is unchanged (the ludic.gameplay controller tests still pass). Verified by `examples/library/namespace_block.ludic` — exported methods dispatch (`15 42 100`), an internal sibling is reachable from inside, and `Combat.base()` from outside is a compile error. `namespace` is registered across the vocabulary (ludic_syntax.h + JetBrains + TextMate + docs page + inventory) so the vocab/impl/docs checks stay green.
orkun closed this issue 2026-09-02 07:15:10 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: workshopsoft/ludic#76
No description provided.