A Tour of WebAssembly Interface Types (wit) Specification
Welcome to Cosmonic's whirlwind tour of wit
!
Jargon Decoder Ring
Before going into more detail on the various aspects of the component model and wit
, we'll define a few important terms that you will not only see in our documentation, but are everywhere in the Bytecode Alliance documentation and GitHub repositories. There's a nice distillation that can help keep all of this new and confusing terminology in perspective as you read through the rest of this documentation:
Packages contain interfaces and worlds, Worlds give directions to interfaces, Interfaces contain functions and types.
You'll use package
s, world
s, and interface
s to describe your components and their requirements. You'll be describing them in wit, or WebAssembly Interface Types. Don't worry if these terms are new to you, you'll get used to them through exposure.
Packages
wit
starts with packages, which are collections of interfaces
and worlds
.
Packages are a logical scoping of interfaces and worlds. Packages need to be unique within the global ecosystem of packages, and so a package identifier has a few key identifying elements:
- A namespace. Namespaces can be used to identify organizations, registries, or portions of well-known standards like
wasi
. Packages that are defined by wasmCloud for wasmCloud-specific functionality would likely usewasmcloud
as the namespace, e.g.wasmcloud:messaging@0.1.0
- A package name. The package name identifies the package within the outer scope of a namespace. For example,
clocks
inwasi:clocks
ormessaging
inwasmcloud:messaging
. - A version (optional). If the version is supplied, it needs to conform to "full" semver rules.
Wit packages are units of distribution and are usually either single .wit
files or a directory containing a number of them. Importing types from one interface into another (even interfaces defined in other packages) is done via the use
keyword:
package wasmcloud:sensors@0.1.0
interface types {
enum sensor_type {
temperature
humidity
pressure
}
}
interface sensor-device {
use types.{sensor_type}
...
}
The syntax for importing types from an external interface just uses the 3 elements of a package identifier, as shown below:
package cosmonic:demo
interface my-interface {
use wasi:http/types.{request, response}
}
Here wasi
is the namespace, http
is the package name, and types
is the interface name. The request
and response
are types imported from the types
interface.
Interfaces
Interfaces are collections of functions and types scoped to a package. Note that interfaces are the only place in which types can be defined. They are contracts that are presumed to be satisfied by external entities. Directionality is added to interfaces as part of the world definition (discussed next).
You can use use
statements, type
definitions, and func
definitions in a component model interface:
package cosmonic:lunarfrontiers
interface types {
use cosmonic:eventsourcing/types.{event, ...}
record position {
latitude: f64,
longitude: f64,
altitude: f64,
// ...
}
record stat {
rover-id: u64,
sensor-id: u64,
stat-time: datetime,
// ...
}
position-changed: func(rover-id: string, position: position) -> result<bool>
}
Whether the lunar rover is being notified of the position-changed
or whether the host (or some other component) is being notified depends entirely on the world definition. Put another way, interfaces are defined without direction, and are only imported or exported by worlds.
Worlds
Worlds are top-level definitions within packages. They apply directionality to interfaces (or individual functions) and can be thought of as the complete description of a component. In fact, when generating code from wit
files, the world is usually the main unit of generation.
package cosmonic:lunarfrontiers
// Produces a "rover" Wasm component
world rover {
import cosmonic:lunarfrontiers/robots
// ...
export cosmonic:lunarfrontiers/botcontrol
}
In this definition, the robots
interface is being imported. This means that in the final component supported by code generation, your code (not the host runtime) will be able to call functions on that interface like set-destination
or turn
or attack
etc. The botcontrol
interface is being exported, which means that the host runtime (or some other component) will be able to call functions on that interface to trigger functionality in the rover like construction-completed
to notify the rover construction finished, position-changed
to notify the rover that its position has changed, or shutdown
to tell a rover to terminate.
The important thing here is not the domain of building lunar rovers. What's important is developing some kind of internal instinct for the flow of function calls when declared via import
and export
within a world.
Now that we've got the basics covered, we can get into some of the fun stuff and start modeling real-world concepts in wit
.