component-model icon indicating copy to clipboard operation
component-model copied to clipboard

Package declaration syntax clarity

Open ouillie opened this issue 5 months ago • 5 comments

The current syntax for a package declaration is this:

package-decl        ::= 'package' ( id ':' )+ id ( '/' id )* ('@' valid-semver)?

The spec doesn't describe the purpose of the ( '/' id )* part, and I think it's misplaced. For instance, in use wasi:io/[email protected].{output-stream};, the slash comes after the package name and is used to denote a sub-package resource (an interface). A package declaration should probably be this:

package-decl        ::= 'package' ( id ':' )+ id ('@' valid-semver)?

If there's something important I'm missing, then it's missing from the spec.

I might also chime in that I think the version and sub-resource components of a use statement could be switched, i.e. use wasi:[email protected]/imports instead of use wasi:cli/[email protected].

ouillie avatar Jul 31 '25 18:07 ouillie

Ok, upon closer reading I noticed the spec include this:

🪺 With "nested namespaces and packages", package names are generalized to look like foo:bar:baz/quux, where bar is a nested namespace of foo and quux is a nested package of baz. See the package declaration section for more details.

I guess I'm just wondering if we want both namespace nesting and package nesting. Seems like having both is unnecessary and potentially confusing. Is there a benefit to enabling foo:bar:baz:quux/quuux and foo:bar/baz/quux/quuux to co-exist? I don't see one, and it seems to me like nested namespaces offer all the encapsulation / conflict avoidance we need.

ouillie avatar Jul 31 '25 20:07 ouillie

(Sorry for the slow reply! Just getting back from vacation) That's a great question. First, as background: since nested namespaces and packages are gated by the 🪺 emoji, this feature isn't implemented or fully baked; it's more suggestive of a direction that we've discussed as a potential solution to some problems folks have brought up but aren't (yet) critical to solve. Thus, nested namespaces/packages might need some refinement before being implemented, ungated and shipped in a release.

That being said, my understanding is that the key difference between the nesting of namespaces and packages comes down to versioning and the atomicity of publication: a namespace has no version--it's just scoping names to avoid collisions--while the top-level package is published as an atomic unit with a version (that covers all nested packages). Thus, an example use case for nested namespaces is per-{user,org} namespaces nested under a top-level github namespace while an example use case for nested packages is publishing a collection related components as an atomic unit. So, e.g., github:luke:component-tools/printer/some-interface where printer is a component (nested inside and exported by the component-tools component) and some-interface is a WIT interface defined and used by printer.

lukewagner avatar Aug 07 '25 15:08 lukewagner

Thanks for the thorough answer. I just wanted to flesh out the design discussion to try to sniff out incidental complexity from necessary complexity. Hopefully my chiming in is more elucidating than distracting.

It seems to me that there are 3 concepts (namespaces, packages, worlds) with a bit of conceptual overlap between them:

  1. Both namespaces and packages can be used for nesting / encapsulation, but one is versioned.
  2. In your example, you said "printer is a component (nested inside and exported by the component-tools component) and some-interface is a WIT interface defined and used by printer." Does your use of the word "component" mean it has some sort of relationship to a world? Does a nested a package correspond to a nested component that could not otherwise be expressed using worlds?

I'm wondering if it could be possible to get away with 2 concepts instead of 3 to hopefully reduce the already-steepening learning curve of the component model, but admittedly don't have a super strong case for that.

ouillie avatar Aug 08 '25 17:08 ouillie

Great question and thanks for digging in!

Currently (as implemented in tooling) a "package" is just a component with metadata published with a name+version to a registry (possibly published/fetched via a registry-specific format/protocol such as the Wasm OCI Artifact Layout but always representable as a single .wasm file as a baseline). Then, the /foo part of ns:pkg/foo is meant to refer to the foo export of the ns:pkg component. Since components can export components, that means that ns:pkg/foo can name another component which itself has exports which may be components etc, hence ns:pkg/foo/bar/baz is just a repeated projection of component exports until reaching baz which may or may not be a component itself. WIT interfaces and worlds are then encoded as component-model type definitions (represented as normal type sections in the component binary format) that are exported with their interface or world names and so publishing a "WIT package" is just a special case of publishing a component where the component only contains type sections and exports (no executable code).

In contrast, namespaces have no semantic meaning or representation according to the component-model; they're just strings that are interpreted by tooling and runtimes and used to avoid name collisions.

lukewagner avatar Aug 08 '25 19:08 lukewagner

Ok, cool. Thanks for all your work on this BTW.

ouillie avatar Aug 09 '25 17:08 ouillie