Skip to content

questions #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion component-model/src/creating-and-consuming/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

You can write WebAssembly core modules in a wide variety of languages, and the set of languages that can directly create components is growing. See the [Language Support](../language-support.md) section for information on building components directly from source code.

If your preferred language supports WebAssembly but not components, you can still create components using the [`wasm-tools component`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) tool. (A future version of this page will cover this in more detail.)
If your preferred language supports WebAssembly but not components, you can still create components using the [`wasm-tools component`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) tool. (A future version of this page will cover this in more detail.)

I think we can update this file to use `wasm-tools component new`.
8 changes: 6 additions & 2 deletions component-model/src/creating-and-consuming/composing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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.

> 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.
> 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 type?, they fit together directly.

## What is composition?

Expand Down Expand Up @@ -47,13 +47,17 @@ If we compose `validator` with `regex`, `validator`'s import of `docs:regex/matc

Component composition tools are in their early stages right now. Here are some tips to avoid or diagnose errors:

* 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.
* Composition happens at the level of interfaces. If the initial component directly imports functions, then composition will fail.
^^^ So this means WIT is nominal typing?
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.
* 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.
* 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:mypackage@0.1.0`. If another Rust component _imports_ an interface from `test:mypackage`, that won't match `test:mypackage@0.1.0`. 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.
^^^ Is there any version unification logic?

## Composing components with WAC

You can use the [WAC](https://github.com/bytecodealliance/wac) CLI to compose components at the command line.
^^^ Seems like WAC can be merged into wasm-tools?

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:

Expand Down
1 change: 1 addition & 0 deletions component-model/src/design/packages.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# WIT Packages
^^^ This file can probably be merged to `WIT.md`? WIT already has a section on package.

A **WIT package** is a set of one or more [WIT (Wasm Interface Type)](./wit.md) files containing a related set of interfaces and worlds. WIT is an IDL (interface definition language) for the Component Model. Packages provide a way for worlds and interfaces to refer to each other, and thus for an ecosystem of components to share common definitions.

Expand Down
23 changes: 23 additions & 0 deletions component-model/src/design/wit.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ list<customer> // a list of customers
This is similar to Rust `Vec`, or Java `List`.

### Options
^^^ Maybe we want to introduce `records` and `variants` first, because `option` and `result` are special case of `record` and `variant`.

`option<T>` for any type `T` may contain a value of type `T`, or may contain no value. `T` can be any type, built-in or user-defined. For example, a lookup function might return an option, allowing for the possibility that the lookup key wasn't found:

Expand Down Expand Up @@ -151,6 +152,7 @@ A `tuple` type is an ordered _fixed length_ sequence of values of specified type
tuple<u64, string> // An integer and a string
tuple<u64, string, u64> // An integer, then a string, then an integer
```
^^^ Is `tuple<>` the same as `_`?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it isn't the same. That is a common question though, so something we should explicitly cover in the doc.


This is similar to tuples in Rust or OCaml.

Expand All @@ -175,6 +177,10 @@ Records are similar to C or Rust `struct`s.

> User-defined records can't be generic (that is, parameterised by type). Only built-in types can be generic.

^^^ Not sure I understand this note. I assume WIT cannot have generic types at all?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wit has a few builtin types which are effectively generic, such as stream<T>, future<T>, result<O, E>, and Option<T>. But it does not support user-defined generics at this time.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. These can be done with syntactic restrictions, and not something the user can define. To me, that's not really a generic, because you cannot define a function that takes stream<T> as an argument. T has to be a concrete type at the interface level.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I think of it is: we have some built in generic types, but we don't support generic functions.

^^^ Do we consider `record T { a: T }` as a valid type, which is isomorphic to empty type?
^^^ Is `record {}` the same as `_`?
Copy link

@dgohman-fastly dgohman-fastly Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's different. I think I said earlier that _ is a unit type, but more precisely: _ is a special-case in result and other types that use it meaning "no value".

The component-model went back and forth on this topic a few times. Should functions return multiple values, which could be zero values? Or should they return a single value which could be a tuple, which could be a 0-tuple? Valid arguments can be made either way. For better or worse, we ended up going with functions that can return multiple results.


### Variants

A `variant` type declares one or more cases. Each case has a name and, optionally, a type of data associated with that case. A variant instance contains exactly one case. Cases are separated by commas. The syntax is as follows:
Expand All @@ -191,6 +197,9 @@ Variants are similar to Rust `enum`s or OCaml discriminated unions. The closest

> User-defined variants can't be generic (that is, parameterised by type). Only built-in types can be generic.

^^^ Do we allow recursive types, e.g., `variant List { head: i32, tail: option<List> }`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not today, but this is a commonly-requested feature that we plan to add in the future.

^^^ Do we allow `variant {}`, which is an empty type?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No; to make it easier for language bindings, I believe we disallow empty variants, because not all source languages have a nice way to express such things.


### Enums

An `enum` type is a variant type where none of the cases have associated data:
Expand All @@ -204,6 +213,7 @@ enum color {
```

This can provide a simpler representation in languages without discriminated unions. For example, a WIT `enum` can translate directly to a C++ `enum`.
^^^ Is `enum` a syntatic sugar of `variant`?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a specialization.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. So it's not quite a syntactic sugar. The underlying representation can be different from the de-specialized types.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's a good point. The semantics of specializations are equivalent to non-specialized forms, but in general, the representation and implementation code can be different. That said, I don't recall enum doing anything different from variant in practice.

An example of a specialization that does differ from its non-specialized form is string; it's equivalent to list<char>, but while list<char> is effectively UTF-32, string can use other encodings such as UTF-8 or UTF-16. They store the same values, just represented differently.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if an interface imports string, and the user provides list<char> type. Does it still compose?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, they are considered distinct types, so they don't compose like that. So you're right, they are not sugar.


### Resources

Expand Down Expand Up @@ -243,6 +253,7 @@ always desugar to an owned return value. For example, the `blob` resource
```wit
resource blob;
blob-constructor: func(bytes: list<u8>) -> blob;
^^^ Why is constructor not a static function?
blob-write: func(self: borrow<blob>, bytes: list<u8>);
blob-read: func(self: borrow<blob>, n: u32) -> list<u8>;
blob-merge: static func(lhs: blob, rhs: blob) -> blob;
Expand All @@ -252,6 +263,7 @@ When a `resource` type name is wrapped with `borrow<...>`, it stands for a
"borrowed" resource. A borrowed resource represents a temporary loan of a resource from the
caller to the callee for the duration of the call. In contrast, when the owner
of an owned resource drops that resource, the resource is destroyed.
^^^ Is `borrow` only allowed on resource types, or we can also say `borrow<struct>`? How do we implement borrowing in languages like C?

> More precisely, these are borrowed or owned `handles` of the resource. Learn more about `handles` in the [upstream component model specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles).

Expand All @@ -269,6 +281,7 @@ flags allowed-methods {
```

> A `flags` type is logically equivalent to a record type where each field is of type `bool`, but it is represented more efficiently (as a bitfield) at the binary level.
^^^ Is there a size limit for `flag`?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment it's 32, just to make things easy for bindings generators. But that could be raised in the future.


### Type aliases

Expand All @@ -278,10 +291,16 @@ You can define a new named type using `type ... = ...`. This can be useful for g
type buffer = list<u8>;
type http-result = result<http-response, http-error>;
```
^^^ Do we allow duplicate type names, interface/world names?

## Functions

A function is defined by a name and a function type. Like in record fields, the name is separated from the type by a colon:
^^^ I assume functions are not first class. Can you do
```
type f = func(message: string);
print: f;
```

```wit
do-nothing: func();
Expand All @@ -299,6 +318,8 @@ lookup: func(store: kv-store, key: string) -> option<string>;
```

A function can have multiple return values. In this case the return values must be named, similar to the parameter list. All return values must be populated (in the same way as tuple or record fields).
^^^ There is an asymmetry in the return values: singleton return doesn't need a name, but multiple returns need names.
^^^ What's the difference between `f: () -> (a: u8, b: u8)` and `f: () -> record { a: u8, b: u8 }`?

```wit
get-customers-paged: func(cont: continuation-token) -> (customers: list<customer>, cont: continuation-token);
Expand Down Expand Up @@ -413,10 +434,12 @@ world glow-in-the-dark-multi-function-device {

// ...but also exports a function to make it glow in the dark
export glow: func(brightness: u8);
^^^ If include already has a function `glow`, does this still work?
}
```

As with `use` directives, you can `include` worlds from other packages.
^^^ Can we include a partial world, like in `use`?

## Packages

Expand Down
6 changes: 5 additions & 1 deletion component-model/src/language-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ working with WebAssembly modules and components.
```

4. Use `wasm-tools` to create a component from the core module, first embedding component metadata
^^^ It doesn't say why we need embed command first. Seems like it's putting the binary representation of wit into a custom section. I guess this should be done by SDK in high level languages.
^^^ Both embed and new don't seem to check the type mismatch between wat and wit.
inside the core module and then encoding the WAT to a Wasm binary.

```sh
Expand All @@ -85,5 +87,7 @@ working with WebAssembly modules and components.
You can "run" a component by calling one of its exports. Hosts and runtimes often only support
running components with certain exports. The [`wasmtime`](https://github.com/bytecodealliance/wasmtime) CLI can only run "command" components, so in
order to run the `add` function above, it first must be composed with a primary "command" component
that calls it. See [documentation on running components](./creating-and-consuming/running.md) for
that calls it. See [documentation on running components](./creating-and-consuming/running.md)
^^^ This reference to `running components` is a bit jumpy. Because the reader has to first understand what is component composition.
for
more details.