You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support multiple imports of the same interface with different names #287
However, it may be desirable to have a single interface definition that is imported or exported under different names in a world or to simply import or export a "known" interface with a different kebab name.
The former is currently not possible in the grammar, but it is possible with copy-and-paste:
Note: there may be ambiguity introduced by extending the extern-type rule in the current wit-parser implementation as right now it allows lexing foo: bar as a package name made up of three tokens (<id>, ':', <id>); a hypothetical import foo: bar:baz/qux in a world might be difficult for it to parse as it will likely see that as an import of a package path with nested namespaces. The wac parser lexes package paths as individual tokens and therefore prohibits whitespace in the path, so that would lex as <import-keyword>, <id>, :, <package-path> rather than <import-keyword>, <package-path>.
In terms of parsing I think that's easy enough to fix with a bit more lookahead on the wit-parser side of things, but another aspect to consider here is round-tripping this construct through the binary encoding of components. I think that may be possible by perhaps exporting an instance type and then using that as the type of the exported concrete instances (or something like that).
Regarding how the WIT<-->binary roundtrip could work (which I think might run into some trouble trying to use imports/exports on first consideration): one alternative (but maybe bad) idea is to add and use an implements annotation on imports and exports, which is an idea that has come up for other reasons separately.
The original idea of implements is that when you have an import like (import "foo" (implements "wasi:http/handler") (instance ...)), the implements attribute is neither part of the name (which is the plainnamefoo) nor the type (which is (instance ...)); it's a third field that gives you a sortof "hint" about the semantic contract of foo. Because it's not a name nor a type, the implements annotation doesn't participate in validation (all typing rules ignore it), and also I think include would fail if you attempt to union two worlds with the same plainname, even if they have the same implements (they don't get unified, whereas they would if they had the same interfacename). Instead, implements could be useful as a hint to developer tooling in some contexts to provide helpful suggestions (say, to fill an autocomplete list if you're writing WAC in an IDE).
So, building on this C-M idea: maybe the WIT-level meaning of this new syntactic form @peterhuene is suggesting (which I agree seems useful) is that import foo: wasi:http/handler turns into (import "foo" (implements "wasi:http/handler") (instance ...)). Thoughts?
That seems plausible to me yeah, the validation of the implements tag would need to happen during WIT decoding-from-wasm but that seems tractable and isn't much more difficult than other forms of extra validation at that layer already happening.
FTR, I made the same observation. If you want WIT to scale, then there really ought to be a declaration form for naming interface types. (And as Luke is aware, I strongly believe that the off interpretation of "interface" in WIT is at odds with doing this in the natural manner. ;) )
Capturing a point made earlier by @dicej for posterity: as soon as you add this feature, you also want a complementary extension to WIT to be able to refer to the plainname identifiers from later uses, so that types can be shared as much as with interfacenames.
On the subject of roundtripping (and, in general, on how these look at the raw component level), I just wanted to capture an idea that came up earlier but I forgot about until this came up recently: instead of adding a whole new "implements" attribute on imports/exports (distinct from the name and type), we could instead put the information into the name in a structured way (similar to what we've been doing with method, constructor, static and, recently, async). For example, for the WIT:
changed the title [-]WIT: world `extern-type` rule is too restrictive[/-][+]Support multiple imports of the same interface with different names[/+]on Jun 5, 2025
Capturing a point made earlier by @dicej for posterity: as soon as you add this feature, you also want a complementary extension to WIT to be able to refer to the plainname identifiers from later uses, so that types can be shared as much as with interfacenames.
When I discovered the awesome #613 but this is the same gap I'm seeing - and it leaves plainname-interfaces especially limited when used resources enter the game.
Consider wasi:http/handler, which uses resources from wasi:http/types:
The resources request and response are defined in wasi:http/types, not in handler itself. Under current world encoding rules a single wasi:http/types import is implicitly injected, and both plain-named imports share the same aliased $request/$response from it — so handles are interchangeable across primary-handler and fallback-handler. That may or may not be the intended semantics, but either way the question doesn't even arise for resources defined within the interface, because there is no way to name them at all.
If wasi:http/handler instead defined its own resource directly:
then each plain-named import gets a fresh abstract request type — per the note in PR #613's Explainer that "resource types defined in the interface are treated as distinct for each import." But since a plain-named import cannot appear as a use-path target, those fresh types are permanently anonymous. There is no way to write:
useprimary-handler.{request}; // no such syntax
to bring them into scope for other type definitions in the world. With an interfacename import you get that for free, since wasi:http/handler can always be used as a use target. With a plain name, the types are effectively sealed inside the instance type — visible to the component model's type checker but unreachable from WIT source.
This creates an asymmetry: plain-named imports are strictly less expressive than interface-named ones whenever the interface exposes resource types, whether defined internally or via use. The complementary use-path extension Luke mentioned seems necessary to close that gap before plain-named imports can be used in practice for any non-trivial WASI interface.
I guess what we need is actually a single, more fundamental extension (at world scope): allow overriding the use dependencies of an interface at the import/export site. Concretely, when writing
import primary-handler: wasi:http/handler with { wasi:http/types => primary-types };
import fallback-handler: wasi:http/handler with { wasi:http/types => fallback-types };
// now needed as well:
import primary-types: wasi:http/types
import fallback-types: wasi:http/types
the with clause substitutes which world import satisfies the wasi:http/types dependency slot for each plain-named import, giving each its own request/response type identity. It should probably also be possible to:
import primary-handler: wasi:http/handler with { wasi:http/types => both-types };
import fallback-handler: wasi:http/handler with { wasi:http/types => primary-handler[wasi:http/types] };
// now needed as well:
import both-types: wasi:http/types
This is not a new binary-level concept — the wrapping component-type in the binary encoding already "parameterizes" an interface's instance type over its use dependencies (WIT.md describes this as "∀T.{instance type}"). So the semantics exist at the component model level; what's missing is WIT syntax to surface them.
Point 1 from above (making plain-named imports addressable via use) then follows as a consequence: once you've named the substituted dependency (primary-types), its types are reachable by use primary-types.{request} like any other world import.
Does it make sense? Is anything beyond #613 happening in that direction?
Thanks @Felix-El; I think you're exactly right. #613 really just exacerbates the need for two existing WIT expressivity gaps which require the two features (with and use-of-plain-names) you mention above. I'm not aware of an imminent plan to work on these, but given the general goal of getting WIT co-expressive with WAT before 1.0 (#614), I think we'll need to. Happy to work with you or anyone else who wants to work on this!
Activity
alexcrichton commentedon Jan 2, 2024
In terms of parsing I think that's easy enough to fix with a bit more lookahead on the
wit-parserside of things, but another aspect to consider here is round-tripping this construct through the binary encoding of components. I think that may be possible by perhaps exporting an instance type and then using that as the type of the exported concrete instances (or something like that).lukewagner commentedon Jan 3, 2024
Regarding how the WIT<-->binary roundtrip could work (which I think might run into some trouble trying to use imports/exports on first consideration): one alternative (but maybe bad) idea is to add and use an
implementsannotation on imports and exports, which is an idea that has come up for other reasons separately.The original idea of
implementsis that when you have an import like(import "foo" (implements "wasi:http/handler") (instance ...)), theimplementsattribute is neither part of the name (which is theplainnamefoo) nor the type (which is(instance ...)); it's a third field that gives you a sortof "hint" about the semantic contract offoo. Because it's not a name nor a type, theimplementsannotation doesn't participate in validation (all typing rules ignore it), and also I thinkincludewould fail if you attempt to union two worlds with the sameplainname, even if they have the sameimplements(they don't get unified, whereas they would if they had the sameinterfacename). Instead,implementscould be useful as a hint to developer tooling in some contexts to provide helpful suggestions (say, to fill an autocomplete list if you're writing WAC in an IDE).So, building on this C-M idea: maybe the WIT-level meaning of this new syntactic form @peterhuene is suggesting (which I agree seems useful) is that
import foo: wasi:http/handlerturns into(import "foo" (implements "wasi:http/handler") (instance ...)). Thoughts?alexcrichton commentedon Jan 4, 2024
That seems plausible to me yeah, the validation of the
implementstag would need to happen during WIT decoding-from-wasm but that seems tractable and isn't much more difficult than other forms of extra validation at that layer already happening.rossberg commentedon Jan 9, 2024
FTR, I made the same observation. If you want WIT to scale, then there really ought to be a declaration form for naming interface types. (And as Luke is aware, I strongly believe that the off interpretation of "interface" in WIT is at odds with doing this in the natural manner. ;) )
lukewagner commentedon Mar 18, 2024
Capturing a point made earlier by @dicej for posterity: as soon as you add this feature, you also want a complementary extension to WIT to be able to refer to the
plainnameidentifiers from lateruses, so that types can be shared as much as withinterfacenames.lukewagner commentedon Mar 19, 2025
On the subject of roundtripping (and, in general, on how these look at the raw component level), I just wanted to capture an idea that came up earlier but I forgot about until this came up recently: instead of adding a whole new "implements" attribute on imports/exports (distinct from the name and type), we could instead put the information into the name in a structured way (similar to what we've been doing with
method,constructor,staticand, recently,async). For example, for the WIT:the WAT could be:
(or some bikeshedded variation thereof).
[-]WIT: world `extern-type` rule is too restrictive[/-][+]Support multiple imports of the same interface with different names[/+]Add [implements=<I>]L plainname for multiple imports
Add [implements=<I>]L for multiple imports
Felix-El commentedon May 4, 2026
When I discovered the awesome #613 but this is the same gap I'm seeing - and it leaves plainname-interfaces especially limited when
usedresourcesenter the game.Consider
wasi:http/handler, which uses resources fromwasi:http/types:The resources
requestandresponseare defined inwasi:http/types, not inhandleritself. Under current world encoding rules a singlewasi:http/typesimport is implicitly injected, and both plain-named imports share the same aliased$request/$responsefrom it — so handles are interchangeable acrossprimary-handlerandfallback-handler. That may or may not be the intended semantics, but either way the question doesn't even arise for resources defined within the interface, because there is no way to name them at all.If
wasi:http/handlerinstead defined its own resource directly:then each plain-named import gets a fresh abstract
requesttype — per the note in PR #613's Explainer that "resource types defined in the interface are treated as distinct for each import." But since a plain-named import cannot appear as ause-path target, those fresh types are permanently anonymous. There is no way to write:to bring them into scope for other type definitions in the world. With an
interfacenameimport you get that for free, sincewasi:http/handlercan always be used as ausetarget. With a plain name, the types are effectively sealed inside the instance type — visible to the component model's type checker but unreachable from WIT source.This creates an asymmetry: plain-named imports are strictly less expressive than interface-named ones whenever the interface exposes resource types, whether defined internally or via
use. The complementaryuse-path extension Luke mentioned seems necessary to close that gap before plain-named imports can be used in practice for any non-trivial WASI interface.I guess what we need is actually a single, more fundamental extension (at world scope): allow overriding the
usedependencies of an interface at the import/export site. Concretely, when writingimport primary-handler: wasi:http/handler with { wasi:http/types => primary-types }; import fallback-handler: wasi:http/handler with { wasi:http/types => fallback-types }; // now needed as well: import primary-types: wasi:http/types import fallback-types: wasi:http/typesthe
withclause substitutes which world import satisfies thewasi:http/typesdependency slot for each plain-named import, giving each its ownrequest/responsetype identity. It should probably also be possible to:import primary-handler: wasi:http/handler with { wasi:http/types => both-types }; import fallback-handler: wasi:http/handler with { wasi:http/types => primary-handler[wasi:http/types] }; // now needed as well: import both-types: wasi:http/typesThis is not a new binary-level concept — the wrapping component-type in the binary encoding already "parameterizes" an interface's instance type over its
usedependencies (WIT.md describes this as "∀T.{instance type}"). So the semantics exist at the component model level; what's missing is WIT syntax to surface them.Point 1 from above (making plain-named imports addressable via
use) then follows as a consequence: once you've named the substituted dependency (primary-types), its types are reachable byuse primary-types.{request}like any other world import.Does it make sense? Is anything beyond #613 happening in that direction?
lukewagner commentedon May 5, 2026
Thanks @Felix-El; I think you're exactly right. #613 really just exacerbates the need for two existing WIT expressivity gaps which require the two features (
withanduse-of-plain-names) you mention above. I'm not aware of an imminent plan to work on these, but given the general goal of getting WIT co-expressive with WAT before 1.0 (#614), I think we'll need to. Happy to work with you or anyone else who wants to work on this!