|
| 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