Skip to content

Add new walkthrough-style explainer - #745

Open
lukewagner wants to merge 16 commits into
rename-explainerfrom
new-explainer
Open

lukewagner wants to merge 16 commits into
rename-explainerfrom
new-explainer

Conversation

@lukewagner

Copy link
Copy Markdown
Member

This commit adds a new Explainer.md that attempts to provide a better introductory experience to the CM for folks already thinking at the WAT level by walking through the CM's (pre-0.3) features, starting with a "hello, world" WAT component and then progressively introducing CM features through new examples. The target audience here is CG folks already familiar with WAT and evaluating this as a new spec proposal and perhaps thinking about runtime/toolchain implementations... not general audiences (for that, component-model.bytecodealliance.org is still linked to as the better starting point).

@eqrion pointed out that a good explainer should also start with a nice crisp updated-for-2026 Problem Statement, but that's something I think we can iterate on separately (perhaps as part of the first one or two CM SG meetings) so for now there's just a short placeholder section at the beginning with a "TODO" and link to design/high-level/ (which have historically sorta served this purpose).

There are also JS API links left as "TODO" that are intended to be resolved by the new Markdown files added in #686, which I'd like to focus on getting merged next, before this PR (at which point I'll rebase and update this PR to link to the new docs); I'm mostly just posting this now to parallelize feedback. Also, in the last CG meeting, @tlively asked if there was any good CM SG "pre-reading" for anyone so motivated and I think perhaps this PR might serve that purpose.

@vados-cosmonic

Copy link
Copy Markdown
Collaborator

This is fantastic -- I did have one ask though, could the new walkthrough style explainer be multiple files rather than one massive one?

Also I do wonder if renaming Explainer.md is a good idea given all the links out there to it...

@lukewagner

Copy link
Copy Markdown
Member Author

@vados-cosmonic Thanks! Personally, I think single-page documents are convenient, because you can ctrl-F skip around the page, see your progress as you go down the page, and see the automatic ToC. Also, the CM repo already uses this single-page-explainer convention. If page-load times were bad, that might be a reason to break it up, but it seems to load quite fast. As for renaming old Explainer.md to Text.md, I think it might not be too bad; I think mostly folks were linking to the top-level repo or README.md or just https://component-model.bytecodealliance.org.

@tlively tlively left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the nice explainer. These comments are mostly about ways to improve the exposition, but a few design questions slipped in there, too. I have plenty more design questions that I'll reserve for a better forum :)

Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated

@rossberg rossberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. Super helpful to be able to understand it without all the WIT idiosyncrasies.

How about including the donut wrapping example? You refer to it several times, and it's almost the only piece missing to make this doc self-contained.

Some suggestions and questions below. Most of the questions are probably outside the scope of this doc, but popped up during my read-through.

Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md Outdated
Comment on lines +760 to +762
`borrow` handle. This optimization applies only when passing a `(borrow $R)`
handle to the component instance that defined `$R` and it skips the overhead of
adding a handle, calling `resource.rep`, and then calling `resource.drop` to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand how this composes. What if a function optimised this way wants to transitively lend the borrow further to a function from outside the component?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the component defined the resource type itself (such that this optimization applies), it can't use the resource type in imports so it can't pass the received borrow handle to an import, and borrowed handles can't be returned, so I think there is noone that the borrowed handle can be given to.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "can't use" do you mean: if used, it would not be possible to supply such an import in a meaningful manner? Or is there some actual restriction?

What if I define a nested component that imports the parent's resource type? That could also have a function taking a borrow for it which the parent might invoke, or am I missing something?

@lukewagner lukewagner Oct 21, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"can't use" means validation rejects an import with a function type that refers to local resource type definition such as:

(component
  (type $R (resource (rep i32)))
  (import "f" (func (param "p" (borrow $R)))) ;; error: func not valid to be used as import
)

The nested-component case is a fair counterpoint though. In this rather-corner case, since the parent is the resource implementer, it can resource.new a fresh handle for the given rep and pass that into the child. Reconsidering this optimization-vs-regularity is a fair question for the CM SG so I'll add a bullet to the future-discussion list.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the parent tries to pass one of these functions themselves as import to the child?

If this optimisation implies a different calling conventions, then I suspect it is fundamentally incompatible with higher-order constructs like nested components. When functions from different origin have indistinguishable type, then you can very likely construct examples where both can flow into the same context (e.g. via some imports & instantiations), and hence require compatible calling conventions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a core-wasm-ABI detail and already callers and callees can have different core wasm ABIs (this is what explicit lifting/lowering is all about), so I'm not sure I see the hazard you're talking about. In particular, the meaning of the lifted call is entirely determined by the relationship between the resource type and the component instance containing the canon lift, so whether it's called from the "outside" or a child component shouldn't matter... but maybe I'm missing your point?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, so it's the component's responsibility to get the origins straight and not confuse lowered functions with a different interpretation of — in this case — the i32 argument.

Perhaps the example would be slightly clearer if you picked i64 as the representation type. Then it's immediately clear that the parameter lowers to something else in those place.

I still wonder whether the component should simply have a choice for how the parameter is lowered.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea; I updated the example to use (rep i64), which ended up being pretty nice for illustrating the general point that the CM encapsulates the type of memory being used.

I've also wondered about this optimization. Ultimately, it's an ABI-level detail so, if we found a reason to want the handle index in the future, it'd be easy enough to add an ABI option that backwards-compatibly turned it of.

Comment thread design/mvp/Explainer.md Outdated
Comment thread design/mvp/Explainer.md
mapping the hierarchical name into the language's existing support for packages,
modules or namespaces. To do this, in addition to the kebab-case **plain names**
shown above, component names can also be **interface names** which are a triplet
of: namespace `:` package name `/` package item name. For example:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some package systems are hierarchical, so shouldn't nested paths be allowed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, and some possible use cases for this (on the producer side) have even been discussed and so the text format grammar is already generalized to allow repeated namespace: and package/ names. However, this hasn't been implemented yet (limited resources and all) and so it's still an emoji-gated feature. I added a short note to mention this possibility here.

Comment thread design/mvp/Explainer.md
Comment on lines +894 to +895
resolve this tension, interface names can alternatively be put in an `implements`
attribute of a plain-named import or export. For example:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit that I'm really confused by how the linking logic is expressed. Some is encoded in the import name, some outside of it with ad-hoc clauses like implements and external-id, some things appear to be expressible in multiple ways. In particular, I didn't get the intended difference between implements and external-id. What does it mean to have both? And what do they default to when absent?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit adds a rationale for what goes in a name vs. in an attribute. This new commit shows both implements and external-id attributes used together and explains the role of each part. It is rather nuanced though and so more discussion in the CM SG is already planned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants