What is a Wasm component?
When I first heard people in the Wasm world talking about these things called "components" for Wasm, I had no idea what they were talking about.
Other people have now written about what a Wasm component is, such as this blog post from Fermyon which makes an analogy relating Wasm concepts to native code and OS concepts, and this podcast with Lin Clark which makes an analogy between Wasm components and Lego blocks. And the Wasm component model proposal repo has documentation about its goals, intended use cases, and design choices, and an FAQ. These are all great resources, and I recommend them.
This blog post aims to provide a simple answer to the question: What is a Wasm component? I answer this question from my own perspective, as someone who didn't previously know anything about component models.
This post is forward-looking; not all of the pieces described here are usable yet. It's a look at what's coming.
What is a Wasm component?
A Wasm component is a deployable unit of software.
This packs a lot of meaning into a few words, so let's unpack!
A Wasm component is a self-contained unit. It doesn't need wrappers, glue, header files, or build artifacts, to be usable. It may have dependencies, but its dependencies are on declared and self-describing APIs, rather than on specific artifacts.
In comparison, Wasm modules often have implicit contracts with the outside
world. When a Wasm module passes around an
i32 value, it might be a number,
which might be signed or unsigned, or it might be a set of flags, a function
pointer, or a data pointer. If it's a data pointer, the pointer is expected
to be used with a particular linear memory address space, where it points to
a region of memory with a layout, a lifetime, and other conventions.
This means that Wasm modules have the flexibility to represent many different kinds of source languages. But it also means that many source-language assumptions aren't represented in a Wasm module's type system. Anyone using a module needs to share those assumptions. This means a Wasm module conceptually acts as a slice of a larger whole.
A Wasm component is a whole. It may still be used as a dependency within a larger conceptual program, but it doesn't depend on being used that way. Components just have APIs which can be used wherever they're relevant.
Wasm's high level of deterministic behavior and isolation provides very consistent behavior, even across diverse environments.
Wasm components build on that. They add features supporting capability-based security, which allows them to share I/O facilities with other components without sharing namespaces or user identity. And they don't use a globally-shared linear memory, which protects against cross-component memory-safety hazards.
Wasm components also add explicit linking features. Linking works the same way whether it's performed at development time, or just before the program is run. The output of linking components is a new component. This new component can then be optimized, including with inlining, dead code elimination, and other optimizations that can then cross the original component boundaries.
This enables the ability to take a collection of Wasm components and either run them as a collection, or link them into a single optimized easy-to-deploy component.
Wasm components are "software", rather than "Python software", "Haskell software", or any other specific source language's software. The source language a Wasm component is implemented in is not exposed in the API of a component.
That's not to say that Wasm components have to always be implemented in software. Components encapsulate their implementation, so component APIs can also be implemented by host environments, including using special hardware. But from the outside, the API behaves the same as if it were software.
A Wasm component is a deployable unit of software!
There's a lot more that can be said about components; check out the links in the intro, and follow me for future explorations!