WebAssembly / component-model

Repository for design and specification of the Component Model
Other
914 stars 78 forks source link

Package identifier syntax #231

Open badeend opened 11 months ago

badeend commented 11 months ago

While developing a syntax highlighter for WIT, I discovered some sources of confusion (for me at least) regarding package identifiers.

The punctuation symbols in package identifiers (specifically : and .) clash visually with other uses of those same symbols. Examples:

export example:http-client // An export named `example` of type `http-client`
export example:http/client // An export named `client` from the package `example:http`
// The tricky part for me is that one symbol, half-way through a line,
// affects the meaning of what was declared _earlier_ in that line.
// This is awkward to implement in TextMate grammars (which are regex!).

// and:

use example:http/client@1.0.0-beta.nginx.{ request, response }

// It may look like we're "drilling into" some sort of `nginx` submodule
// of the `example:http/client@1.0.0-beta` package, but actually `nginx` is part of the
// semver 'prerelease' component. The actual package id here is: `example:http/client@1.0.0-beta.nginx`
// Moreover, the last period __is__ a "drilling into" period (the one before the curly brace)

One solution could be to enclose package identifiers in quotes like JS and Go do. That way it is immediately clear where the identifier starts and ends, even for developers not intimately familiar with Wit syntax. Tangentially related; in the example below I've hoisted the interface selector (/client) out of the package name, so that package identifier strings are the same everywhere, regardless of where in the wit file they're used.

export example: http-client  // An export named `example` of type `http-client`
export "example:http".client // An export named `client` from the package `example:http`

// and:

use "example:http@1.0.0-beta.nginx".client.{ request, response }
lann commented 11 months ago

Agreed that versions make the use syntax pretty gnarly. My only concern with quotes is that it sort of suggests that the contents aren't part of the WIT syntax.

lukewagner commented 11 months ago

Coincidentally I was also just unhappily realizing the giant difference a space makes between export foo: bar and export foo:bar and also wondering whether we should require quotes around these registry names. Your other example reinforces this, so I'm open to the change. I'd be interested to hear what @alexcrichton and other Wit-heavy folks think.

lukewagner commented 11 months ago

Oops, I missed Lann's reply. Yes, that was the original reason we wanted to leave them off, since these are very structured names, not just any literal. Maybe there's a better bracketing character?

badeend commented 11 months ago

My only concern with quotes is that it sort of suggests that the contents aren't part of the WIT syntax.

Fair point. Any other notation will do.

export example: http-client  // An export named `example` of type `http-client`
export [example:http].client // An export named `client` from the package `example:http`

// and:

use [example:http@1.0.0-beta.nginx].client.{ request, response }
alexcrichton commented 11 months ago

One thing perhaps worth pointing out is that export foo:bar is "old syntax" at this point and not currently used in WIT. Nowadays that's only supported in worlds as export foo: func(..) or export foo: interface { ... } which alleviates that problem somewhat I think. I do think, though, that the use syntax is not great.

One other possibility is to disallow the use syntax and require:

use example:http/client@1.0.0-beta.nginx as client

basically requiring that projection doesn't happen in use statements of interface but only at the top level of files. This is already supported but isn't currently required.

As for other colors of the bikeshed, there's always the sigil-heavy option of use foo:bar@1.0.0[some-iface]{the, names} but I think that's a bit silly. Otherwise I don't have many shades of paint to offer myself.

lann commented 11 months ago

requiring that projection doesn't happen in use statements of interface but only at the top level of files

Veering just a bit off-topic, if top-level usees brought their symbols into package (rather than file) scope, this could also be a nice way to implement a "wit.toml" without introducing a new format. This would be a "strong convention" for multi-file WIT packages to stick all their dependency uses in an e.g. package.wit file along with the single package item and no other items.

alexcrichton commented 11 months ago

Most of my experience with languages so far (in general) has been with Rust/C/C++ where changes in one file don't affect names in scope for another (e.g. Rust modules are all in their own little worlds), so I may be biased in this regard but I'd personally find it surprising for notation in one file to affect another. In that sense I'd be hesitant to support what you're thinking, although I do realize that WIT's kind of a single-file-per-package anyway so I can also see how it wouldn't be the most surprising thing in the world