Skip to content

Commit bfd59e8

Browse files
authored
feat: Add 'WIT by Example' section (#287)
* Add 'WIT by Example' section * Simplify examples * Change WIT examples to use wasi-example namespace * Add comments in interfaces/worlds with links to original packages
1 parent c420ce1 commit bfd59e8

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package wasi-example:clocks;
2+
3+
/// The following is a simplified copy of an interface from wasi:clocks.
4+
/// For the full version, see https://github.com/WebAssembly/wasi-clocks/tree/main/wit
5+
interface wall-clock {
6+
record datetime {
7+
seconds: u64,
8+
nanoseconds: u32,
9+
}
10+
11+
now: func() -> datetime;
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package wasi-example:clocks;
2+
3+
/// The following is a simplified copy of a world from wasi:clocks.
4+
/// For the full version, see https://github.com/WebAssembly/wasi-clocks/tree/main/wit
5+
world imports {
6+
import wall-clock;
7+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package wasi-example:filesystem;
2+
3+
/// The following is a simplified copy of an interface from wasi:filesystems.
4+
/// For the full version, see https://github.com/WebAssembly/wasi-filesystem/tree/main/wit
5+
interface types {
6+
7+
enum error-code {
8+
access,
9+
bad-descriptor,
10+
}
11+
12+
resource descriptor {
13+
read: func(
14+
length: filesize,
15+
offset: filesize,
16+
) -> result<tuple<list<u8>, bool>, error-code>;
17+
18+
open-at: func(
19+
path: string,
20+
) -> result<descriptor, error-code>;
21+
22+
}
23+
}

component-model/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [Interfaces](./design/interfaces.md)
1212
- [Worlds](./design/worlds.md)
1313
- [Packages](./design/packages.md)
14+
- [WIT By Example](./design/wit-example.md)
1415
- [WIT Reference](./design/wit.md)
1516

1617
# Using WebAssembly Components
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# WIT By Example
2+
3+
This section includes two examples to introduce WIT:
4+
a simpler "clocks" example and a more complicated "filesystems" example.
5+
For a full WIT reference, see [the next section](./wit.md).
6+
7+
## Clocks
8+
9+
The following is a simplified version of the world defined in
10+
the [wasi:clocks](https://github.com/WebAssembly/wasi-clocks) package.
11+
12+
Suppose we want to write a component that provides clock functionality.
13+
This component will represent a "wall clock", which can be reset
14+
(the clock is not monotonic).
15+
(The real `wasi:clocks` package provides two interfaces,
16+
one for a wall clock and one for a monotonic clock.)
17+
18+
### Declaring a world
19+
20+
We declare a world that imports one interface:
21+
22+
```wit
23+
{{#include ../../examples/wit-section-examples/clocks/world.wit}}
24+
```
25+
26+
For exposition, version numbers have been removed.
27+
28+
This file contains a package declaration, which declares that
29+
this world is in the `clocks` package in the `wasi` namespace.
30+
31+
The world is declared using the keyword `world`, followed by
32+
the name `imports`.
33+
World declarations must begin with `world`, but the name `imports`
34+
is an arbitrary choice.
35+
What follows is a list of `import` declarations enclosed in curly braces,
36+
each of which consists of the `import` keyword
37+
followed by the name of an interface.
38+
Each declaration is followed by a semicolon.
39+
40+
### Declaring an interface: `wall-clock`
41+
42+
```wit
43+
{{#include ../../examples/wit-section-examples/clocks/wall-clock.wit}}
44+
```
45+
46+
Like a world, an interface is declared with a keyword (`interface`) in this case,
47+
followed by a name, followed by a semicolon-separated list of declarations enclosed
48+
in curly braces.
49+
In this case, declarations are _type declarations_ or _function declarations_.
50+
51+
52+
### Type declarations
53+
54+
_Record types_ are one of the possible types that can be declared in WIT.
55+
56+
```wit
57+
record datetime {
58+
seconds: u64,
59+
nanoseconds: u32,
60+
}
61+
```
62+
63+
The `record` keyword is followed by a name, then by a list of
64+
field declarations separated by commas.
65+
Each field declaration is a field name (a string), followed by
66+
a colon, followed by a type name.
67+
68+
A record is analogous to a `struct` in C or Rust,
69+
in that it groups together named fields.
70+
It is also analogous to a JavaScript object, except
71+
that it has no methods or prototype.
72+
73+
In short, the `datetime` type is a record with two fields:
74+
`seconds`, an unsigned 64-bit integer, and `nanoseconds`,
75+
an unsigned 32-bit integer.
76+
77+
### Function declarations
78+
79+
The following declares a function named `now`:
80+
81+
```wit
82+
now: func() -> instant;
83+
```
84+
85+
The empty parentheses `()` indicate that the function has no arguments.
86+
The return type is the type after the final arrow (`->`),
87+
which is `instant`.
88+
Putting it together: `now()` is a nullary function that returns an instant.
89+
90+
### Summing up
91+
92+
The `imports` world contains an interface for wall clocks.
93+
(Real worlds usually contain multiple interfaces.)
94+
The wall clock world defines a record type that represents a time value
95+
in terms of seconds and nanoseconds,
96+
as well as a function to get the current time.
97+
98+
99+
## WIT By Example: Filesystems
100+
101+
That was just a warm-up; let's look at an example that uses
102+
more of WIT's built-in and user-defined types.
103+
104+
The following is a very simplified version of the main interface
105+
defined in the [wasi-filesystem](https://github.com/WebAssembly/wasi-filesystem) package.
106+
Much of the functionality has been removed.
107+
Here, a file descriptor supports just two operations:
108+
* `open-at()`: Open a file.
109+
* `read()`: Read from a file, starting at a particular offset.
110+
111+
```wit
112+
{{#include ../../examples/wit-section-examples/filesystems/types.wit}}
113+
```
114+
115+
Let's look at some WIT features used in this interface.
116+
117+
### Enums
118+
119+
```wit
120+
enum error-code {
121+
access,
122+
bad-descriptor,
123+
}
124+
```
125+
126+
This declaration defines an enumeration type named `error-code`
127+
with two alternatives: `access` and `bad-descriptor`.
128+
The contents of the curly brackets is just a list of comma-separated names.
129+
Enum types are similar to enums in C, and are useful for
130+
expressing types that have a known, small set of values.
131+
This declaration expresses the possible error codes
132+
that filesystem operations can return.
133+
In reality, there are many more possible errors,
134+
which would be expressed by adding more alternatives to the enumeration.
135+
136+
### Resources
137+
138+
A resource describes an interface for objects.
139+
This is not the same kind of "interface" as a WIT interface;
140+
a WIT interface can contain many different `resource` declarations.
141+
The declaration of the `descriptor` resource says that
142+
a `descriptor` is an object that implements two methods:
143+
`read` and `open-at`.
144+
Let's look at the method declarations one at a time:
145+
146+
#### Reading from files
147+
148+
```wit
149+
read: func(
150+
length: filesize,
151+
offset: filesize,
152+
) -> result<tuple<list<u8>, bool>, error-code>;
153+
```
154+
155+
Method declarations use the same syntax as regular function declarations,
156+
like the ones we already saw in the clocks example.
157+
This declaration says that the `read()` method has two arguments,
158+
`length` and `offset`, both of which have type `filesize`.
159+
The return type of `read` is a `result`.
160+
161+
`result` is another parameterized type, like `option`.
162+
Let's look at the parameters before we look at the entire type:
163+
* `list` is also a parameterized type; in this case,
164+
it's applied to `u8` (unsigned 8-bit integer),
165+
so `list<u8>` can be read as "list of bytes".
166+
* `tuple` is like a list with a known size,
167+
whose elements can have different types.
168+
`tuple<list<u8>, bool>` represents a 2-tuple (pair)
169+
of a list of bytes and a boolean.
170+
* `error-code` was defined as an `enum` type.
171+
172+
If `a` and `b` are both types, then `result<a, b>` represents
173+
a type that can be either `a` or `b`.
174+
Often, but not always, `b` is a type that represents an error,
175+
like in this case.
176+
So the type `result<tuple<list<u8>, bool>, error-code>` means
177+
"either a tuple of a list of bytes and a bool; or an error code".
178+
179+
This makes sense for the `read()` function because it takes a
180+
number of bytes to read and an offset within a file to start at;
181+
and the result is either an error, or a list of bytes containing
182+
the data read from the file,
183+
paired with a boolean indicating whether the end of the file was
184+
reached.
185+
186+
#### Opening files
187+
188+
The `open-at()` method is a constructor, which we know because
189+
it returns a `descriptor` when it doesn't fail (remember that
190+
these methods are attached to the resource type `descriptor`):
191+
192+
```wit
193+
open-at: func(
194+
path: string,
195+
) -> result<descriptor, error-code>;
196+
```
197+
198+
`open-at()` returns a new descriptor, given a path string and flags.
199+
200+
## Further reading
201+
202+
We've seen how using rich types, WIT can encode a multitude
203+
of ideas about how functions interrelate,
204+
which are not available in the type system of core WebAssembly.
205+
206+
For more WIT examples, see the [tutorial](../tutorial.md) section.
207+
The next section, [WIT Reference](./wit.md), covers WIT syntax
208+
more thoroughly.

0 commit comments

Comments
 (0)