From 5a4760b2bbacd573a2fefc6c7ba080e7fccbdb5e Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 21 Jul 2025 14:47:57 -0700 Subject: [PATCH 1/4] Add 'WIT by Example' section --- .../clocks/monotonic-clock.wit | 29 ++ .../clocks/wall-clock.wit | 30 ++ .../wit-section-examples/clocks/world.wit | 6 + .../filesystems/types.wit | 94 +++++ component-model/src/SUMMARY.md | 1 + component-model/src/design/wit-example.md | 358 ++++++++++++++++++ 6 files changed, 518 insertions(+) create mode 100644 component-model/examples/wit-section-examples/clocks/monotonic-clock.wit create mode 100644 component-model/examples/wit-section-examples/clocks/wall-clock.wit create mode 100644 component-model/examples/wit-section-examples/clocks/world.wit create mode 100644 component-model/examples/wit-section-examples/filesystems/types.wit create mode 100644 component-model/src/design/wit-example.md diff --git a/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit b/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit new file mode 100644 index 00000000..c800939c --- /dev/null +++ b/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit @@ -0,0 +1,29 @@ +package wasi:clocks@0.2.6; +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +interface monotonic-clock { + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + type instant = u64; + + /// A duration of time, in nanoseconds. + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + resolution: func() -> duration; +} diff --git a/component-model/examples/wit-section-examples/clocks/wall-clock.wit b/component-model/examples/wit-section-examples/clocks/wall-clock.wit new file mode 100644 index 00000000..13bf0500 --- /dev/null +++ b/component-model/examples/wit-section-examples/clocks/wall-clock.wit @@ -0,0 +1,30 @@ +package wasi:clocks; +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + resolution: func() -> datetime; +} diff --git a/component-model/examples/wit-section-examples/clocks/world.wit b/component-model/examples/wit-section-examples/clocks/world.wit new file mode 100644 index 00000000..888b9c26 --- /dev/null +++ b/component-model/examples/wit-section-examples/clocks/world.wit @@ -0,0 +1,6 @@ +package wasi:clocks; + +world imports { + import monotonic-clock; + import wall-clock; +} diff --git a/component-model/examples/wit-section-examples/filesystems/types.wit b/component-model/examples/wit-section-examples/filesystems/types.wit new file mode 100644 index 00000000..e092e564 --- /dev/null +++ b/component-model/examples/wit-section-examples/filesystems/types.wit @@ -0,0 +1,94 @@ +package wasi:filesystem; +interface types { + use wasi:clocks/wall-clock.{datetime}; + + /// File size or length of a region within a file. + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + enum descriptor-type { + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a regular file inode. + regular-file, + } + + /// File attributes. + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// File size in bytes. + size: filesize, + /// Last data access timestamp (optional). + data-access-timestamp: option, + } + + /// Open flags used by `open-at`. + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + } + + /// When setting a timestamp, this gives the value to set it to. + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + } + + /// A descriptor is a reference to a filesystem object, which may be a + /// file or directory. + resource descriptor { + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. + read: func( + /// The maximum number of bytes to read. + length: filesize, + /// The offset within the file at which to read. + offset: filesize, + ) -> result, bool>, error-code>; + + /// Return the attributes of an open file or directory. + stat: func() -> result; + + /// Adjust the timestamps of a file or directory. + set-times-at: func( + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + ) -> result<_, error-code>; + + /// Open a file or directory. + open-at: func( + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + ) -> result; + + } +} diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index 0bb86d31..a5b94939 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -11,6 +11,7 @@ - [Interfaces](./design/interfaces.md) - [Worlds](./design/worlds.md) - [Packages](./design/packages.md) +- [WIT By Example](./design/wit-example.md) - [WIT Reference](./design/wit.md) # Using WebAssembly Components diff --git a/component-model/src/design/wit-example.md b/component-model/src/design/wit-example.md new file mode 100644 index 00000000..a9356f2d --- /dev/null +++ b/component-model/src/design/wit-example.md @@ -0,0 +1,358 @@ +# WIT By Example + +This section includes two examples to introduce WIT: +a simpler "clocks" example and a more complicated "filesystems" example. +For a full WIT reference, see [the next section](./wit.md). + +## Clocks + +The following is a simplified version of the world defined in +the [wasi-clocks](https://github.com/WebAssembly/wasi-clocks) package. + +Suppose we want to write a component that provides clock functionality. +Our component will provide two sets of functionality, +each corresponding to its own interface: +* A monotonic clock, whose time is guaranteed to always increase in value. +* A wall clock, which can be reset. + +### Declaring a world + +We declare a world that imports two interfaces: + +```wit +{{#include ../../examples/wit-section-examples/clocks/world.wit}} +``` + +For exposition, version numbers have been removed. + +This file contains a package declaration, which declares that +this world is in the `clocks` package in the `wasi` namespace. + +The world is declared using the keyword `world`, followed by +the name `imports`. +World declarations must begin with `world`, but the name `imports` +is an arbitrary choice. +What follows is a list of `import` declarations enclosed in curly braces, +each of which consists of the `import` keyword +followed by the name of an interface. +Each declaration is followed by a semicolon. + +### Declaring an interface: `monotonic-clock` + +A separate file, `monotonic-clock.wit`, defines the `monotonic-clock` +interface: + +```wit +{{#include ../../examples/wit-section-examples/clocks/monotonic-clock.wit}} +``` + +Anything beginning with `///` is a comment. + +Like a world, an interface is declared with a keyword (`interface`) in this case, +followed by a name, followed by a semicolon-separated list of declarations enclosed +in curly braces. +In this case, declarations are _type declarations_ or _function declarations_. + +### Type declarations + +The following is an example of a _type alias_: + +```wit +type instant = u64; +``` + +Type declarations can be more complex, but this example only uses type aliases. +This declaration declares a type named `instant` that is a synonym for the type `u64` +(unsigned 64-bit integers). +In other words, this interface represents an instant in time as +an unsigned 64-bit integer. +The declaration for `duration` is similar. + +### Function declarations + +The following declares a function named `now`: + +```wit +now: func() -> instant; +``` + +The empty parentheses `()` indicate that the function has no arguments. +The return type is the type after the final arrow (`->`), +which is `instant`. +Putting it together: `now()` is a nullary function that returns an instant. + +Similarly, `resolution()` is a nullary function that returns a duration. + +### Declaring an interface: `wall-clock` + +```wit +{{#include ../../examples/wit-section-examples/clocks/wall-clock.wit}} +``` + +This interface looks similar to `monotonic-clock`, declaring a type `datetime` +and two functions `now` and `resolution`, but it contains a more complicated +type declaration. + +### More type declarations + +```wit +record datetime { + seconds: u64, + nanoseconds: u32, +} +``` + +The `record` keyword is followed by a name, then by a list of +field declarations separated by commas. +Each field declaration is a field name (a string), followed by +a colon, followed by a type name. + +A record is analogous to a `struct` in C or Rust, +in that it groups together named fields. +It is also analogous to a JavaScript object, except +that it has no methods or prototype. + +In short, the `datetime` type is a record with two fields: +`seconds`, an unsigned 64-bit integer, and `nanoseconds`, +an unsigned 32-bit integer. + +### Summing up + +The `imports` world contains two interfaces, one for monotonic clocks +and one for wall clocks. +The monotonic clock world defines types to represent instants and durations, +along with functions to get the current time and the resolution of the clock. +The wall clock world defines a record type that represents a time value +differently, +as well as functions to get the current time and the resolution of the clock +that return this record type. + + +## WIT By Example: Filesystems + +That was just a warm-up; let's look at an example that uses +more of WIT's built-in and user-defined types. + +The following is a simplified version of the main interface +defined in the [wasi-filesystem](https://github.com/WebAssembly/wasi-filesystem) package. +Much of the functionality has been removed in order to show +the minimal amount of functionality to exhibit all of the +types that are available in WIT. +Many of the types have been simplified by removing +some of the alternatives from enums +and some of the fields from records. +We've even removed the ability to write to files. +Here, a file descriptor supports four operations: +* `open-at()`: Open a file. +* `read()`: Read from a file. +* `stat()`: Get file metadata. +* `set-times-at()`: Set the access time on a file. + +```wit +{{#include ../../examples/wit-section-examples/filesystems/types.wit}} +``` + +Let's look at the contents of this interface piece by piece. + +### Enums + +```wit +enum descriptor-type { + directory, + regular-file, +} +``` + +This declaration defines an enumeration type named `descriptor-type` +with two alternatives: `directory` and `regular-file`. +The contents of the curly brackets is just a list of comma-separated names. +Enum types are similar to enums in C, and are useful for +expressing types that have a known, small set of values. +This declaration expresses that a descriptor type +can be either a directory or a regular file. +This corresponds to a simplified model of a filesystem +where files can either be directories or non-directories, +with no other file types. + +### Options + +```wit +record descriptor-stat { + %type: descriptor-type, + size: filesize, + data-access-timestamp: option, +} +``` + +This declaration expresses the metadata for a file descriptor. +In our simplified example, that metadata correspond to +the type of the file, the size of the file, and the timestamp +at which the file was last accessed. + +We already encountered record types, but this record type +has a new type in one of its fields, `option`. +The `option` type is parameterized, like templates in C++ or generics in Java: +you can read `option` as "option of datetime". +It represents a value that may or may not be present, +and is used here to model the fact that some filesystems +don't track access timestamps. + +Note that the `datetime` type is defined by the `wasi:clocks/wall-clock` interface, +as indicated by the `use` declaration at the beginning of the `types` interface. + +### Flags + +```wit +flags open-flags { + create, + directory, +} +``` + +A `flags` type is similar to a `record` type, but with +the restriction that all the fields have type `bool` (boolean). +Flags are represented efficiently, using bitfields, at runtime. +This type declaration says that there are two flags used +for opening a file, +`create` and `directory`. +This means that when opening a file, we can either create it if it doesn't exist, +or error out. +Also, we can open it as a directory, or as a regular file. +We can have all four possible combinations of values +of these two boolean flags. + +### Variants + +```wit +variant new-timestamp { + no-change, + now, + timestamp(datetime), +} +``` + +As we'll see, this type is used by the +`set-times-at` function that mutates the timestamp of a file. +A variant type is similar to an `enum` in Rust +or an algebraic datatype in Haskell or ML. +The closest C equivalent is a tagged union. + +This declaration is saying that a `new-timestamp` can either be +the tag `no-change` with no argument, +the tag `now` with no argument, +or a `datetime` tagged with the tag `timestamp`. +The implementations of functions that use this type can +use pattern-matching to simultaneously check the tag +and extract the contents (if applicable). + +### Putting it all together: resources + +A resource describes an interface for objects. +This is not the same kind of "interface" as a WIT interface; +a WIT interface can contain many different `resource` declarations. +The declaration of the `descriptor` resource says that +a `descriptor` is an object that implements four methods: +`read`, `stat`, `set-times-at`, and `open-at`. +Let's look at the method declarations one at a time: + +#### Reading from files + +```wit +read: func( + length: filesize, + offset: filesize, +) -> result, bool>, error-code>; +``` + +Method declarations use the same syntax as regular function declarations, +like the ones we already saw in the clocks example. +This declaration says that the `read()` method has two arguments, +`length` and `offset`, both of which have type `filesize`. +The return type of `read` is a `result`. + +`result` is another parameterized type, like `option`. +Let's look at the parameters before we look at the entire type: +* `list` is also a parameterized type; in this case, + it's applied to `u8` (unsigned 8-bit integer), + so `list` can be read as "list of bytes". +* `tuple` is like a list with a known size, + whose elements can have different types. + `tuple, bool>` represents a 2-tuple (pair) + of a list of bytes and a boolean. +* `error-code` was defined as an `enum` type. + +If `a` and `b` are both types, then `result` represents +a type that can be either `a` or `b`. +Often, but not always, `b` is a type that represents an error, +like in this case. +So the type `result, bool>, error-code>` means +"either a tuple of a list of bytes and a bool; or an error code". + +This makes sense for the `read()` function because it takes a +number of bytes to read and an offset within a file to start at; +and the result is either an error, or a list of bytes containing +the data read from the file, +paired with a boolean indicating whether the end of the file was +reached. + +#### Getting metadata about files. + +```wit +stat: func() -> result; +``` + +The next method, `stat()`, gets metadata about a file. +It has no arguments (other than the implicit object argument +that all non-constructor methods have). +Like `read()`, it can fail, so it returns a `result` type. +The result can be either the `descriptor-stat` record we +already saw, or an error. + +#### Setting timestamps on files + +```wit +set-times-at: func( + path: string, + data-access-timestamp: new-timestamp, +) -> result<_, error-code>; +``` + +The next method, `set-times-at()`, sets the timestamp on a file. +Like the previous two methods, it can fail, so it returns a `result` type. +The result, if not an error, is `_`; this type represents the absence of a value, +like `void` in C/C++. + +The function has two arguments: a file path, which is a string, +and a timestamp to set on the file at this path, +which is expressed using the `new-timestamp` variant type +that we already saw. +Looking at the definition of `new-timestamp`, we can see that +there are three different behaviors this method can implement: +either not changing the timestamp; setting it to the current time; +or setting it to a given time other than the current time. + +#### Opening files + +The `open-at()` method is a constructor, which we know because +it returns a `descriptor` when it doesn't fail (remember that +these methods are attached to the resource type `descriptor`): + +```wit +open-at: func( + path: string, + open-flags: open-flags, +) -> result; +``` + +`open-at()` returns a new descriptor, given a path string and flags. + +## Further reading + +We've seen how using records, variants, enums, options, lists, tuples, +resources, and other rich types, WIT can encode a multitude of +ideas about how functions interrelate, +which are not available in the type system of core WebAssembly. + +For more WIT examples, see the [tutorial](../tutorial.md) section. +The next section, [WIT Reference](./wit.md), covers WIT syntax +more thoroughly. From 12caf68aaa7dd960a6727f8dac73843904e91e96 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Tue, 12 Aug 2025 12:39:46 -0700 Subject: [PATCH 2/4] Simplify examples --- .../clocks/wall-clock.wit | 22 +- .../wit-section-examples/clocks/world.wit | 1 - .../filesystems/types.wit | 74 ------ component-model/src/design/wit-example.md | 242 ++++-------------- 4 files changed, 47 insertions(+), 292 deletions(-) diff --git a/component-model/examples/wit-section-examples/clocks/wall-clock.wit b/component-model/examples/wit-section-examples/clocks/wall-clock.wit index 13bf0500..676a17ce 100644 --- a/component-model/examples/wit-section-examples/clocks/wall-clock.wit +++ b/component-model/examples/wit-section-examples/clocks/wall-clock.wit @@ -1,30 +1,10 @@ package wasi:clocks; -/// WASI Wall Clock is a clock API intended to let users query the current -/// time. + interface wall-clock { - /// A time and date in seconds plus nanoseconds. record datetime { seconds: u64, nanoseconds: u32, } - /// Read the current value of the clock. - /// - /// This clock is not monotonic, therefore calling this function repeatedly - /// will not necessarily produce a sequence of non-decreasing values. - /// - /// The returned timestamps represent the number of seconds since - /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], - /// also known as [Unix Time]. - /// - /// The nanoseconds field of the output is always less than 1000000000. - /// - /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 - /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time now: func() -> datetime; - - /// Query the resolution of the clock. - /// - /// The nanoseconds field of the output is always less than 1000000000. - resolution: func() -> datetime; } diff --git a/component-model/examples/wit-section-examples/clocks/world.wit b/component-model/examples/wit-section-examples/clocks/world.wit index 888b9c26..9ff44df4 100644 --- a/component-model/examples/wit-section-examples/clocks/world.wit +++ b/component-model/examples/wit-section-examples/clocks/world.wit @@ -1,6 +1,5 @@ package wasi:clocks; world imports { - import monotonic-clock; import wall-clock; } diff --git a/component-model/examples/wit-section-examples/filesystems/types.wit b/component-model/examples/wit-section-examples/filesystems/types.wit index e092e564..1ae49594 100644 --- a/component-model/examples/wit-section-examples/filesystems/types.wit +++ b/component-model/examples/wit-section-examples/filesystems/types.wit @@ -1,93 +1,19 @@ package wasi:filesystem; interface types { - use wasi:clocks/wall-clock.{datetime}; - /// File size or length of a region within a file. - type filesize = u64; - - /// The type of a filesystem object referenced by a descriptor. - enum descriptor-type { - /// The descriptor refers to a directory inode. - directory, - /// The descriptor refers to a regular file inode. - regular-file, - } - - /// File attributes. - record descriptor-stat { - /// File type. - %type: descriptor-type, - /// File size in bytes. - size: filesize, - /// Last data access timestamp (optional). - data-access-timestamp: option, - } - - /// Open flags used by `open-at`. - flags open-flags { - /// Create file if it does not exist, similar to `O_CREAT` in POSIX. - create, - /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. - directory, - } - - /// When setting a timestamp, this gives the value to set it to. - variant new-timestamp { - /// Leave the timestamp set to its previous value. - no-change, - /// Set the timestamp to the current time of the system clock associated - /// with the filesystem. - now, - /// Set the timestamp to the given value. - timestamp(datetime), - } - - /// Error codes returned by functions, similar to `errno` in POSIX. enum error-code { - /// Permission denied, similar to `EACCES` in POSIX. access, - /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. - would-block, - /// Connection already in progress, similar to `EALREADY` in POSIX. - already, - /// Bad descriptor, similar to `EBADF` in POSIX. bad-descriptor, - /// Device or resource busy, similar to `EBUSY` in POSIX. - busy, } - /// A descriptor is a reference to a filesystem object, which may be a - /// file or directory. resource descriptor { - /// Read from a descriptor, without using and updating the descriptor's offset. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a bool which, when true, indicates that the end of the - /// file was reached. read: func( - /// The maximum number of bytes to read. length: filesize, - /// The offset within the file at which to read. offset: filesize, ) -> result, bool>, error-code>; - /// Return the attributes of an open file or directory. - stat: func() -> result; - - /// Adjust the timestamps of a file or directory. - set-times-at: func( - /// The relative path of the file or directory to operate on. - path: string, - /// The desired values of the data access timestamp. - data-access-timestamp: new-timestamp, - ) -> result<_, error-code>; - - /// Open a file or directory. open-at: func( - /// The relative path of the object to open. path: string, - /// The method by which to open the file. - open-flags: open-flags, ) -> result; } diff --git a/component-model/src/design/wit-example.md b/component-model/src/design/wit-example.md index a9356f2d..45298b36 100644 --- a/component-model/src/design/wit-example.md +++ b/component-model/src/design/wit-example.md @@ -7,17 +7,17 @@ For a full WIT reference, see [the next section](./wit.md). ## Clocks The following is a simplified version of the world defined in -the [wasi-clocks](https://github.com/WebAssembly/wasi-clocks) package. +the [wasi:clocks](https://github.com/WebAssembly/wasi-clocks) package. Suppose we want to write a component that provides clock functionality. -Our component will provide two sets of functionality, -each corresponding to its own interface: -* A monotonic clock, whose time is guaranteed to always increase in value. -* A wall clock, which can be reset. +This component will represent a "wall clock", which can be reset +(the clock is not monotonic). +(The real `wasi:clocks` package provides two interfaces, +one for a wall clock and one for a monotonic clock.) ### Declaring a world -We declare a world that imports two interfaces: +We declare a world that imports one interface: ```wit {{#include ../../examples/wit-section-examples/clocks/world.wit}} @@ -37,63 +37,21 @@ each of which consists of the `import` keyword followed by the name of an interface. Each declaration is followed by a semicolon. -### Declaring an interface: `monotonic-clock` - -A separate file, `monotonic-clock.wit`, defines the `monotonic-clock` -interface: +### Declaring an interface: `wall-clock` ```wit -{{#include ../../examples/wit-section-examples/clocks/monotonic-clock.wit}} +{{#include ../../examples/wit-section-examples/clocks/wall-clock.wit}} ``` -Anything beginning with `///` is a comment. - Like a world, an interface is declared with a keyword (`interface`) in this case, followed by a name, followed by a semicolon-separated list of declarations enclosed in curly braces. In this case, declarations are _type declarations_ or _function declarations_. -### Type declarations - -The following is an example of a _type alias_: - -```wit -type instant = u64; -``` - -Type declarations can be more complex, but this example only uses type aliases. -This declaration declares a type named `instant` that is a synonym for the type `u64` -(unsigned 64-bit integers). -In other words, this interface represents an instant in time as -an unsigned 64-bit integer. -The declaration for `duration` is similar. -### Function declarations - -The following declares a function named `now`: - -```wit -now: func() -> instant; -``` - -The empty parentheses `()` indicate that the function has no arguments. -The return type is the type after the final arrow (`->`), -which is `instant`. -Putting it together: `now()` is a nullary function that returns an instant. - -Similarly, `resolution()` is a nullary function that returns a duration. - -### Declaring an interface: `wall-clock` - -```wit -{{#include ../../examples/wit-section-examples/clocks/wall-clock.wit}} -``` - -This interface looks similar to `monotonic-clock`, declaring a type `datetime` -and two functions `now` and `resolution`, but it contains a more complicated -type declaration. +### Type declarations -### More type declarations +_Record types_ are one of the possible types that can be declared in WIT. ```wit record datetime { @@ -116,16 +74,26 @@ In short, the `datetime` type is a record with two fields: `seconds`, an unsigned 64-bit integer, and `nanoseconds`, an unsigned 32-bit integer. +### Function declarations + +The following declares a function named `now`: + +```wit +now: func() -> instant; +``` + +The empty parentheses `()` indicate that the function has no arguments. +The return type is the type after the final arrow (`->`), +which is `instant`. +Putting it together: `now()` is a nullary function that returns an instant. + ### Summing up -The `imports` world contains two interfaces, one for monotonic clocks -and one for wall clocks. -The monotonic clock world defines types to represent instants and durations, -along with functions to get the current time and the resolution of the clock. +The `imports` world contains an interface for wall clocks. +(Real worlds usually contain multiple interfaces.) The wall clock world defines a record type that represents a time value -differently, -as well as functions to get the current time and the resolution of the clock -that return this record type. +in terms of seconds and nanoseconds, +as well as a function to get the current time. ## WIT By Example: Filesystems @@ -133,126 +101,46 @@ that return this record type. That was just a warm-up; let's look at an example that uses more of WIT's built-in and user-defined types. -The following is a simplified version of the main interface +The following is a very simplified version of the main interface defined in the [wasi-filesystem](https://github.com/WebAssembly/wasi-filesystem) package. -Much of the functionality has been removed in order to show -the minimal amount of functionality to exhibit all of the -types that are available in WIT. -Many of the types have been simplified by removing -some of the alternatives from enums -and some of the fields from records. -We've even removed the ability to write to files. -Here, a file descriptor supports four operations: +Much of the functionality has been removed. +Here, a file descriptor supports just two operations: * `open-at()`: Open a file. -* `read()`: Read from a file. -* `stat()`: Get file metadata. -* `set-times-at()`: Set the access time on a file. +* `read()`: Read from a file, starting at a particular offset. ```wit {{#include ../../examples/wit-section-examples/filesystems/types.wit}} ``` -Let's look at the contents of this interface piece by piece. +Let's look at some WIT features used in this interface. ### Enums ```wit -enum descriptor-type { - directory, - regular-file, +enum error-code { + access, + bad-descriptor, } ``` -This declaration defines an enumeration type named `descriptor-type` -with two alternatives: `directory` and `regular-file`. +This declaration defines an enumeration type named `error-code` +with two alternatives: `access` and `bad-descriptor`. The contents of the curly brackets is just a list of comma-separated names. Enum types are similar to enums in C, and are useful for expressing types that have a known, small set of values. -This declaration expresses that a descriptor type -can be either a directory or a regular file. -This corresponds to a simplified model of a filesystem -where files can either be directories or non-directories, -with no other file types. - -### Options - -```wit -record descriptor-stat { - %type: descriptor-type, - size: filesize, - data-access-timestamp: option, -} -``` - -This declaration expresses the metadata for a file descriptor. -In our simplified example, that metadata correspond to -the type of the file, the size of the file, and the timestamp -at which the file was last accessed. - -We already encountered record types, but this record type -has a new type in one of its fields, `option`. -The `option` type is parameterized, like templates in C++ or generics in Java: -you can read `option` as "option of datetime". -It represents a value that may or may not be present, -and is used here to model the fact that some filesystems -don't track access timestamps. - -Note that the `datetime` type is defined by the `wasi:clocks/wall-clock` interface, -as indicated by the `use` declaration at the beginning of the `types` interface. - -### Flags - -```wit -flags open-flags { - create, - directory, -} -``` - -A `flags` type is similar to a `record` type, but with -the restriction that all the fields have type `bool` (boolean). -Flags are represented efficiently, using bitfields, at runtime. -This type declaration says that there are two flags used -for opening a file, -`create` and `directory`. -This means that when opening a file, we can either create it if it doesn't exist, -or error out. -Also, we can open it as a directory, or as a regular file. -We can have all four possible combinations of values -of these two boolean flags. +This declaration expresses the possible error codes +that filesystem operations can return. +In reality, there are many more possible errors, +which would be expressed by adding more alternatives to the enumeration. -### Variants - -```wit -variant new-timestamp { - no-change, - now, - timestamp(datetime), -} -``` - -As we'll see, this type is used by the -`set-times-at` function that mutates the timestamp of a file. -A variant type is similar to an `enum` in Rust -or an algebraic datatype in Haskell or ML. -The closest C equivalent is a tagged union. - -This declaration is saying that a `new-timestamp` can either be -the tag `no-change` with no argument, -the tag `now` with no argument, -or a `datetime` tagged with the tag `timestamp`. -The implementations of functions that use this type can -use pattern-matching to simultaneously check the tag -and extract the contents (if applicable). - -### Putting it all together: resources +### Resources A resource describes an interface for objects. This is not the same kind of "interface" as a WIT interface; a WIT interface can contain many different `resource` declarations. The declaration of the `descriptor` resource says that -a `descriptor` is an object that implements four methods: -`read`, `stat`, `set-times-at`, and `open-at`. +a `descriptor` is an object that implements two methods: +`read` and `open-at`. Let's look at the method declarations one at a time: #### Reading from files @@ -295,42 +183,6 @@ the data read from the file, paired with a boolean indicating whether the end of the file was reached. -#### Getting metadata about files. - -```wit -stat: func() -> result; -``` - -The next method, `stat()`, gets metadata about a file. -It has no arguments (other than the implicit object argument -that all non-constructor methods have). -Like `read()`, it can fail, so it returns a `result` type. -The result can be either the `descriptor-stat` record we -already saw, or an error. - -#### Setting timestamps on files - -```wit -set-times-at: func( - path: string, - data-access-timestamp: new-timestamp, -) -> result<_, error-code>; -``` - -The next method, `set-times-at()`, sets the timestamp on a file. -Like the previous two methods, it can fail, so it returns a `result` type. -The result, if not an error, is `_`; this type represents the absence of a value, -like `void` in C/C++. - -The function has two arguments: a file path, which is a string, -and a timestamp to set on the file at this path, -which is expressed using the `new-timestamp` variant type -that we already saw. -Looking at the definition of `new-timestamp`, we can see that -there are three different behaviors this method can implement: -either not changing the timestamp; setting it to the current time; -or setting it to a given time other than the current time. - #### Opening files The `open-at()` method is a constructor, which we know because @@ -340,7 +192,6 @@ these methods are attached to the resource type `descriptor`): ```wit open-at: func( path: string, - open-flags: open-flags, ) -> result; ``` @@ -348,9 +199,8 @@ open-at: func( ## Further reading -We've seen how using records, variants, enums, options, lists, tuples, -resources, and other rich types, WIT can encode a multitude of -ideas about how functions interrelate, +We've seen how using rich types, WIT can encode a multitude +of ideas about how functions interrelate, which are not available in the type system of core WebAssembly. For more WIT examples, see the [tutorial](../tutorial.md) section. From 1183a9638c9c89dfd92dcc77c88cc43d9a99c9d9 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 13 Aug 2025 12:10:58 -0700 Subject: [PATCH 3/4] Change WIT examples to use wasi-example namespace --- .../clocks/monotonic-clock.wit | 29 ------------------- .../clocks/wall-clock.wit | 2 +- .../wit-section-examples/clocks/world.wit | 2 +- .../filesystems/types.wit | 2 +- 4 files changed, 3 insertions(+), 32 deletions(-) delete mode 100644 component-model/examples/wit-section-examples/clocks/monotonic-clock.wit diff --git a/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit b/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit deleted file mode 100644 index c800939c..00000000 --- a/component-model/examples/wit-section-examples/clocks/monotonic-clock.wit +++ /dev/null @@ -1,29 +0,0 @@ -package wasi:clocks@0.2.6; -/// WASI Monotonic Clock is a clock API intended to let users measure elapsed -/// time. -/// -/// It is intended to be portable at least between Unix-family platforms and -/// Windows. -/// -/// A monotonic clock is a clock which has an unspecified initial value, and -/// successive reads of the clock will produce non-decreasing values. -interface monotonic-clock { - - /// An instant in time, in nanoseconds. An instant is relative to an - /// unspecified initial value, and can only be compared to instances from - /// the same monotonic-clock. - type instant = u64; - - /// A duration of time, in nanoseconds. - type duration = u64; - - /// Read the current value of the clock. - /// - /// The clock is monotonic, therefore calling this function repeatedly will - /// produce a sequence of non-decreasing values. - now: func() -> instant; - - /// Query the resolution of the clock. Returns the duration of time - /// corresponding to a clock tick. - resolution: func() -> duration; -} diff --git a/component-model/examples/wit-section-examples/clocks/wall-clock.wit b/component-model/examples/wit-section-examples/clocks/wall-clock.wit index 676a17ce..3bff97d4 100644 --- a/component-model/examples/wit-section-examples/clocks/wall-clock.wit +++ b/component-model/examples/wit-section-examples/clocks/wall-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks; +package wasi-example:clocks; interface wall-clock { record datetime { diff --git a/component-model/examples/wit-section-examples/clocks/world.wit b/component-model/examples/wit-section-examples/clocks/world.wit index 9ff44df4..a4192692 100644 --- a/component-model/examples/wit-section-examples/clocks/world.wit +++ b/component-model/examples/wit-section-examples/clocks/world.wit @@ -1,4 +1,4 @@ -package wasi:clocks; +package wasi-example:clocks; world imports { import wall-clock; diff --git a/component-model/examples/wit-section-examples/filesystems/types.wit b/component-model/examples/wit-section-examples/filesystems/types.wit index 1ae49594..c09973e3 100644 --- a/component-model/examples/wit-section-examples/filesystems/types.wit +++ b/component-model/examples/wit-section-examples/filesystems/types.wit @@ -1,4 +1,4 @@ -package wasi:filesystem; +package wasi-example:filesystem; interface types { enum error-code { From 605a2e1b731b1a2b84b58b913fb62fd486c68df0 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 13 Aug 2025 12:16:53 -0700 Subject: [PATCH 4/4] Add comments in interfaces/worlds with links to original packages --- .../examples/wit-section-examples/clocks/wall-clock.wit | 2 ++ component-model/examples/wit-section-examples/clocks/world.wit | 2 ++ .../examples/wit-section-examples/filesystems/types.wit | 3 +++ 3 files changed, 7 insertions(+) diff --git a/component-model/examples/wit-section-examples/clocks/wall-clock.wit b/component-model/examples/wit-section-examples/clocks/wall-clock.wit index 3bff97d4..40366987 100644 --- a/component-model/examples/wit-section-examples/clocks/wall-clock.wit +++ b/component-model/examples/wit-section-examples/clocks/wall-clock.wit @@ -1,5 +1,7 @@ package wasi-example:clocks; +/// The following is a simplified copy of an interface from wasi:clocks. +/// For the full version, see https://github.com/WebAssembly/wasi-clocks/tree/main/wit interface wall-clock { record datetime { seconds: u64, diff --git a/component-model/examples/wit-section-examples/clocks/world.wit b/component-model/examples/wit-section-examples/clocks/world.wit index a4192692..9caaa6f2 100644 --- a/component-model/examples/wit-section-examples/clocks/world.wit +++ b/component-model/examples/wit-section-examples/clocks/world.wit @@ -1,5 +1,7 @@ package wasi-example:clocks; +/// The following is a simplified copy of a world from wasi:clocks. +/// For the full version, see https://github.com/WebAssembly/wasi-clocks/tree/main/wit world imports { import wall-clock; } diff --git a/component-model/examples/wit-section-examples/filesystems/types.wit b/component-model/examples/wit-section-examples/filesystems/types.wit index c09973e3..cd4866b6 100644 --- a/component-model/examples/wit-section-examples/filesystems/types.wit +++ b/component-model/examples/wit-section-examples/filesystems/types.wit @@ -1,4 +1,7 @@ package wasi-example:filesystem; + +/// The following is a simplified copy of an interface from wasi:filesystems. +/// For the full version, see https://github.com/WebAssembly/wasi-filesystem/tree/main/wit interface types { enum error-code {