Skip to content

Commit 71059eb

Browse files
refactor: Edit "Composing and Distributing Components" section for clarity (#314)
* Edit "Composing and Distributing Components" section for clarity * Update component-model/src/composing-and-distributing/composing.md Co-authored-by: Victor Adossi <[email protected]> --------- Co-authored-by: Victor Adossi <[email protected]>
1 parent ec0daed commit 71059eb

File tree

7 files changed

+242
-117
lines changed

7 files changed

+242
-117
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//composition.wac
2+
// Provide a package name for the resulting composition
3+
package docs:composition;
4+
5+
// Instantiate the regex-impl component that implements the `regex` world.
6+
// Bind this instance's exports to the local name `regex`.
7+
let regex = new docs:regex-impl { };
8+
9+
// Instantiate the validator-impl component which implements the `validator` world
10+
// and imports the match interface from the regex component.
11+
let validator = new docs:validator-impl { match: regex.match, ... };
12+
13+
// Export all remaining exports of the validator instance
14+
export validator...;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package foo:wasi-http-service;
2+
3+
world target-world {
4+
include wasi:http/proxy@0.2.3;
5+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// component 'regex'
2+
package docs:regex@0.1.0;
3+
4+
interface match {
5+
first-match: func(regex: string, text: string) -> string;
6+
}
7+
8+
world regex {
9+
export match;
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// component `validator`
2+
package docs:validator@0.1.0;
3+
4+
interface validator {
5+
validate-text: func(text: string) -> string;
6+
}
7+
8+
world validator {
9+
export validator;
10+
import docs:regex/match@0.1.0;
11+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Composing and Distributing Components
22

3-
The component model defines how components interface to each other and to hosts. This section describes how to work with components - from authoring them in custom code or by composing existing components, through to using them in applications and distributing them via registries.
3+
The component model defines how components interface to each other and to hosts.
4+
This section describes how to work with components: from authoring them in custom code or by composing existing components,
5+
to using them in applications and distributing them via registries.
Lines changed: 106 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,157 @@
11
# Composing Components
22

3-
Because the WebAssembly component model packages code in a portable binary format, and provides machine-readable interfaces in [WIT](../design/wit.md) with a standardised ABI (Application Binary Interface), it enables applications and components to work together, no matter what languages they were originally written in. In the same way that, for example, a Rust package (crate) can be compiled together with other Rust code to create a higher-level library or an application, a Wasm component can be linked with other components.
4-
5-
> Component model interoperation is more convenient and expressive than language-specific foreign function interfaces. A typical C FFI involves language-specific types, so it is not possible to link between arbitrary languages without at least some C-language wrapping or conversion. The component model, by contrast, provides a common way of expressing interfaces, and a standard binary representation of those interfaces. So if an import and an export have the same shape, they fit together directly.
3+
The WebAssembly component model enables applications and components to work together,
4+
no matter what languages they were originally written in.
5+
The component model accomplishes this by packaging code in a portable binary format
6+
and providing machine-readable interfaces in [WIT](../design/wit.md)
7+
with a standardised Application Binary Interface (ABI).
8+
In the same way that, for example, a Rust package (crate) can be compiled together with other Rust code
9+
to create a higher-level library or an application, a WebAssembly component can be composed with other components.
10+
11+
> Component model interoperation is more convenient and expressive than language-specific foreign function interfaces (FFIs).
12+
> A typical C FFI involves language-specific types, so it is not possible to link between arbitrary languages
13+
> without at least some C-language wrapping or conversion.
14+
> The component model, by contrast, provides a common way of expressing interfaces,
15+
> and a standard binary representation of those interfaces.
16+
> So if an import and an export have the same shape, they fit together directly.
617
718
## What is composition?
819

9-
When you compose components, you wire up the imports of one "primary" component to the exports of one or more other "dependency" components, creating a new component. The new component, like the original components, is a `.wasm` file, and its interface is defined as:
20+
When you compose components, you wire up the imports of one _primary_ component
21+
to the exports of one or more other _dependency_ components, creating a new component.
22+
The new component, like the original components, is a `.wasm` file, and its interface is defined as follows:
1023

11-
* The new component _exports_ the same exports as the primary component
12-
* The new component _does not export_ the exports of the dependencies
13-
* The new component _imports_ all the imports of the dependency components
14-
* The new component _imports_ any imports of the primary component imports that the dependencies didn't satisfy
15-
* If several components import the same interface, the new component imports that interface - it doesn't "remember" that the import was declared in several different places
24+
* The new component _exports_ the same exports as the primary component.
25+
* The new component _does not export_ the exports of the dependency components.
26+
* The new component _imports_ all the imports of the dependency components.
27+
* The new component _imports_ any imports of the primary component
28+
that the dependencies didn't satisfy.
29+
* If several components import the same interface,
30+
the new component imports that interface—it doesn't "remember"
31+
that the import was declared in several different places.
1632

1733
For example, consider two components with the following worlds:
1834

1935
```wit
20-
// component `validator`
21-
package docs:[email protected];
22-
23-
interface validator {
24-
validate-text: func(text: string) -> string;
25-
}
26-
27-
world validator {
28-
export validator;
29-
import docs:regex/[email protected];
30-
}
36+
{{#include ../../examples/composing-section-examples/validator.wit}}
3137
```
3238

3339
```wit
34-
// component 'regex'
35-
package docs:[email protected];
36-
37-
interface match {
38-
first-match: func(regex: string, text: string) -> string;
39-
}
40-
41-
world regex {
42-
export match;
43-
}
40+
{{#include ../../examples/composing-section-examples/regex.wit}}
4441
```
4542

46-
If we compose `validator` with `regex`, `validator`'s import of `docs:regex/[email protected]` is wired up to `regex`'s export of `match`. The net result is that the composed component exports `docs:validator/[email protected]` and has no imports. The composed component does _not_ export `docs:regex/[email protected]` - that has become an internal implementation detail of the composed component.
47-
48-
Component composition tools are in their early stages right now. Here are some tips to avoid or diagnose errors:
49-
50-
* Composition happens at the level of interfaces. If the initial component directly imports functions, then composition will fail. If composition reports an error such as "component `path/to/component` has a non-instance import named `<name>`" then check that all imports and exports are defined by interfaces.
51-
* Composition is asymmetrical. It is not just "gluing components together" - it takes a primary component which has imports, and satisfies its imports using dependency components. For example, composing an implementation of `validator` with an implementation of `regex` makes sense because `validator` has a dependency that `regex` can satisfy; doing it the other way round doesn't work, because `regex` doesn't have any dependencies, let alone ones that `validator` can satisfy.
52-
* Composition cares about interface versions, and current tools are inconsistent about when they infer or inject versions. For example, if a Rust component exports `test:mypackage`, `cargo component build` will decorate this with the crate version, e.g. `test:[email protected]`. If another Rust component _imports_ an interface from `test:mypackage`, that won't match `test:[email protected]`. You can use [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the imports and exports embedded in the `.wasm` files and check whether they match up.
43+
In this example, `validator` is the primary component
44+
and `regex` is a dependency component.
45+
If we compose `validator` with `regex`, `validator`'s import of `docs:regex/[email protected]`
46+
is wired up to `regex`'s export of `match`.
47+
The net result is that the composed component exports `docs:validator/[email protected]` and has no imports.
48+
The composed component does _not_ export `docs:regex/[email protected]`: that has become an internal implementation detail of the composed component.
49+
50+
Component composition tools are in their early stages right now.
51+
Here are some tips to avoid or diagnose errors:
52+
53+
* Composition happens at the level of interfaces.
54+
If the initial component directly imports functions, then composition will fail.
55+
If composition reports an error such as
56+
"component `path/to/component` has a non-instance import named `<name>`",
57+
then check that all imports and exports are defined by interfaces.
58+
* Composition is asymmetrical. It is not just "gluing components together"—it takes a primary component
59+
that has imports, and satisfies its imports using dependency components.
60+
For example, composing an implementation of `validator` with an implementation of `regex` makes sense
61+
because `validator` has a dependency that `regex` can satisfy; doing it the other way around doesn't work,
62+
because `regex` doesn't have any dependencies, let alone ones that `validator` can satisfy.
63+
* Composition cares about interface versions, and current tools are inconsistent about
64+
when they infer or inject versions.
65+
For example, if a Rust component exports `test:mypackage`,
66+
`cargo component build` will decorate this with the crate version, e.g. `test:[email protected]`.
67+
If another Rust component _imports_ an interface from `test:mypackage`, that won't match `test:[email protected]`.
68+
You can use [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component)
69+
to view the imports and exports embedded in the `.wasm` files and check whether they match up.
5370

5471
## Composing components with WAC
5572

56-
You can use the [WAC](https://github.com/bytecodealliance/wac) CLI to compose components at the command line.
73+
You can use the [WebAssembly Compositions (WAC)](https://github.com/bytecodealliance/wac) CLI
74+
to compose components at the command line.
5775

58-
To perform quick and simple compositions, use the `wac plug` command. `wac plug` satisfies the import of a "socket" component by plugging a "plug" component's export into the socket. For example, a component that implements the [`validator` world above](#what-is-composition) needs to satisfy it's `match` import. It is a socket. While a component that implements the `regex` world, exports the `match` interface, and can be used as a plug. `wac plug` can plug a regex component's export into the validator component's import, creating a resultant composition:
76+
To perform quick and simple compositions, use the `wac plug` command.
77+
`wac plug` satisfies the import of a "socket" component by plugging a "plug" component's export into the socket.
78+
The socket component is the primary component, while the plug components are dependency components.
79+
For example, a component that implements the [`validator` world above](#what-is-composition)
80+
needs to satisfy its `match` import. It is a socket.
81+
On the other hand, a component that implements the `regex` world exports the `match` interface,
82+
and can be used as a plug.
83+
`wac plug` can plug a regex component's export into the validator component's import,
84+
creating a resultant composition:
5985

6086
```sh
6187
wac plug validator-component.wasm --plug regex-component.wasm -o composed.wasm
6288
```
6389

64-
A component can also be composed with two components it depends on.
90+
A component can also be composed with more than component that it depends on.
6591

6692
```sh
6793
wac plug path/to/component.wasm --plug path/to/dep1.wasm --plug path/to/dep2.wasm -o composed.wasm
6894
```
6995

70-
Here `component.wasm` is the component that imports interfaces from `dep1.wasm` and `dep2.wasm`, which export them. The composed component, with those dependencies satisfied and tucked away inside it, is saved to `composed.wasm`.
96+
Here `component.wasm` is the component that imports interfaces from `dep1.wasm` and `dep2.wasm`,
97+
which export interfaces.
98+
The composed component, with those dependencies satisfied and tucked away inside it, is saved to `composed.wasm`.
7199

72-
The `plug` syntax doesn't cover transitive dependencies. If, for example, `dep1.wasm` has unsatisfied imports that you want to satisfy from `dep3.wasm`, you'd need to be deliberate about the order of your composition. You could compose `dep1.wasm` with `dep3.wasm` first, then refer to that composed component instead of `dep1.wasm`. However, this doesn't scale to lots of transitive dependencies, which is why the WAC language was created.
100+
The `plug` syntax doesn't cover transitive dependencies.
101+
If, for example, `dep1.wasm` has unsatisfied imports that you want to satisfy from `dep3.wasm`,
102+
you'd need to be deliberate about the order of your composition.
103+
You could compose `dep1.wasm` with `dep3.wasm` first, then refer to that composed component instead of `dep1.wasm`.
104+
However, this doesn't scale to lots of transitive dependencies, which is why the WAC language was created.
73105

74106
### Advanced composition with the WAC language
75107

76-
`wac plug` is a convenience to achieve a common pattern in component compositions like above. However, composition can be arbitrarily complicated. In cases where `wac plug` is not sufficient, the [WAC language](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md) can give us the ability to create arbitrarily complex compositions.
108+
`wac plug` is a convenience to achieve a common pattern in component compositions like the ones above.
109+
However, composition can be arbitrarily complicated.
110+
In cases where `wac plug` is not sufficient, the [WAC language](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md)
111+
gives us the ability to create arbitrarily complex compositions.
77112

78-
In a WAC file, you use the WAC language to describe a composition. For example, the following is a WAC file that could be used to create that validator component from [earlier](#what-is-composition).
113+
In a WAC file, you use the WAC language to describe a composition.
114+
For example, the following is a WAC file that could be used to create the validator component from [earlier](#what-is-composition).
79115

80116
```
81-
//composition.wac
82-
// Provide a package name for the resulting composition
83-
package docs:composition;
84-
85-
// Instantiate the regex-impl component that implements the `regex` world. Bind this instance's exports to the local name `regex`.
86-
let regex = new docs:regex-impl { };
87-
88-
// Instantiate the validator-impl component which implements the `validator` world and imports the match interface from the regex component.
89-
let validator = new docs:validator-impl { match: regex.match, ... };
90-
91-
// Export all remaining exports of the validator instance
92-
export validator...;
117+
{{#include ../../examples/composing-section-examples/composition.wac}}
93118
```
94119

95-
Then, `wac compose` can be used to compose the components, passing in the paths to the components. Alternatively, you can place the components in a `deps` directory with an expected structure, and in the near future, you will be able to pull in components from registries. See the [`wac` documentation](https://github.com/bytecodealliance/wac) for more details.
120+
Then, `wac compose` can be used to compose the components, using the `--dep` flag to specify
121+
the relationships between component names and `.wasm` files:
96122

97123
```sh
98-
wac compose --dep docs:regex-impl=regex-component.wasm --dep docs:validator-impl=validator-component.wasm -o composed.wasm composition.wac
124+
wac compose --dep docs:regex-impl=regex-component.wasm \
125+
--dep docs:validator-impl=validator-component.wasm \
126+
-o composed.wasm \
127+
composition.wac
99128
```
100129

101-
For an in depth description about how to use the wac tool, you can check out the [wac language index](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md) and [examples](https://github.com/bytecodealliance/wac/tree/main/examples).
130+
Alternatively, you can place the components in a `deps` directory with an expected structure,
131+
and in the near future, you will be able to pull in components from registries.
132+
See the [`wac` documentation](https://github.com/bytecodealliance/wac) for more details.
133+
134+
For an in-depth description about how to use the `wac` tool,
135+
you can check out the [WAC language index](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md)
136+
and [examples](https://github.com/bytecodealliance/wac/tree/main/examples).
102137

103138
## Composing components with a visual interface
104139

105140
You can compose components visually using the builder app at [wasmbuilder.app](https://wasmbuilder.app/).
106141

107-
1. Use the Add Component Button to upload the `.wasm` component files you want to compose. The components appear in the sidebar.
142+
1. Use the "Add Component" button to upload the `.wasm` component files you want to compose.
143+
The components appear in the sidebar.
108144

109-
2. Drag the components onto the canvas. You'll see imports listed on the left of each component, and exports on the right.
145+
2. Drag the components onto the canvas.
146+
You'll see imports listed on the left of each component, and exports on the right.
110147

111-
3. Click the box in the top left to choose the 'primary' component, that is, the one whose exports will be preserved. (The clickable area is quite small - wait for the cursor to change from a hand to a pointer.)
148+
3. Click the box in the top left to choose the 'primary' component, that is,
149+
the one whose exports will be preserved.
150+
(The clickable area is quite small—wait for the cursor to change from a hand to a pointer.)
112151

113-
4. To fulfil one of the primary component's imports with a dependency's export, drag from the "I" icon next to the export to the "I" item next to the import. (Again, the clickable area is quite small - wait for the cursor to change from a hand to a cross.)
152+
4. To fulfil one of the primary component's imports with a dependency's export,
153+
drag from the "I" icon next to the export to the "I" item next to the import.
154+
(Again, the clickable area is quite small—wait for the cursor to change from a hand to a cross.)
114155

115-
5. When you have connected all the imports and exports that you want, click the Download Component button to download the composed component as a `.wasm` file.
156+
5. When you have connected all the imports and exports that you want,
157+
click the Download Component button to download the composed component as a `.wasm` file.

0 commit comments

Comments
 (0)