top | item 40463063

(no title)

coderedart | 1 year ago

Actually, looking at the https://component-model.bytecodealliance.org/design/wit.html is the best way of understanding why we have what we have.

We have the basic primitives like signed and unsigned integers of various sizes, floating point numbers, bools and chars.

Remember that wasm components are like shared libraries (dll/so objects). Shared libraries themselves have dependencies (eg: vlc needs qt, which needs mesa, which needs x11/wayland etc..). Each component/shared library has a set of imports from other shared libraries and exports items to other shared libraries or apps.

For example, lets say that I want give a vector/array as an argument or receive it as the return type. On native libs, we just give a pointer + len as arguments, and the function can simply read/write using that pointer.

Except, wasm components are isolated (shared-nothing model). So, I can't just allocate the bytes and give a "pointer" to the jpeg decoder. Because both of us don't share the memory. This has a few reasons:

1. security: separate memories make sure that a component can't read/write another component's memory. 2. safety: If we don't have higher level types, then people will just pass around blobs and cast those bytes into types. Imagine one component thinks of rect as `Rect { x, y, w, h: f32 }` and another component thinks `Rect { x1, y1, x2, y2: f32}`. Without "record" types, we can't find that error. 3. flexibility: Lets say we want to pass around a list of strings between components. how would you do it between rust and js? To let each language feel natural, we need to "copy" these higher level types across the boundary (and wasm needs to understand these higher level types) into the respective suitable types.

This is why we have records (structs), strings, list<T>, variants (tagged unions), Option<T> and such types to allow for a feature-rich API. These are all passed by copy at the boundary with proper validation by the runtime in advance, so you get both performance, safety and ergonomics.

Finally, we also need to talk about "ownership", because some objects (like files via file descriptors) need to be passed across component boundary and we need to wasm let somehow know that we are passing on ownership of the file. We do this with "resource" (object with an ownership like a file descriptor or host native object or socket etc..). And wasm must also ensure that the object will be alive for the duration of the borrow.

The rest of the WIT is simple.

Interface is literally just a group of type signatures or objects. just like a module in python, rust. In C world, we usually just prefix the library/type name for all the function that belong to a certain library/type. In WASI, we just place the related fns inside an interface.

Similarly, a world is just a group of imports and exports that represent a component. world = component. world can import/export types/interfaces/fns/data. You can have multiple 'worlds' and interfaces within a wit file.

And a package is a group of wit files. similar to a java or go package that a file belongs to.

Its not really hard to understand. Most of the terminology directly translates to what we all see in any modern language like js, py, java, rust, go etc..

And the docs are not accessible at the moment, because its still a WIP and unstable. They are experimenting with rust and js to see how well this model works in practice.

discuss

order

No comments yet.