Each of the exercises below has a problem where your goal is to upload a component and solve the problem at hand. Select an exercise, read it over, and then upload a component to execute it via the button below. The goal is to learn a bit about the Component Model while getting some hands-on experience with the tooling.
Each exercise has a description of a
WIT world
which indicates what the "shape" of uploaded components must look like.
Components have access to WASI interfaces by default (e.g. they can print
to stdout) and the world
for each exercise will describe
anything additional that the upload can use. Some interfaces, such as WASI
sockets, are not available on the web at this time (this is also sort of
demo quality still).
Exercises will log the components output and/or errors to the "Console output" section below. The goal of each exercise is to upload a component that prints the expected output. If the expected output is not produced or an error happens the "red X" will light up. When the exercise is completed the green checkmark will light up instead.
If you're stuck or otherwise are more curious to poke around you can find the source code of these exercises online which includes example solutions in Rust at this time as well.
To learn about components here you need to upload a component which
prints "Hello, world!", with a newline, to the console.
Printing should happen via standard language I/O interfaces. The
WIT world
for available to this exercie's components is below.
package example:demo world hello { // ... nothing extra for this exercise }
Now that you've built a single component, let's start exploring the component model. Here you're still going to print a greeting, but you need to greet someone specific to the test. The way this will be done is that you're going to import a function from the outer environment which returns a name that you're going to greet.
Think of this as a sort of rudimentary plugin system. The
world
below describes the plugin API (just a single
function) and you're trying to consume this function.
Your job is to have a component which prints "Hello, $name!" when the "$name" comes from the `who-to-greet` function imported into the component. This example will run the component twice to ensure that the correct name isn't hardcoded. The name to print is additionally randomized each time.
package example:demo world hello-name { import who-to-greet: func() -> string }
One of the major features of the component model is the grammar of types that you have available to you. For example in addition to strings which we've already seen the component model supports lists, records, and variants. These map into guest and host languages as appropriate for each language.
Additionally the component model supports types called "resources" which are handles to a type implemented somewhere else. They're similar to file descriptors in Unix and come with static and dynamic guarantees about validity and non-forgability in the component model.
In this exercise your task is to explore this type structure in the component model. The imports into the world need to be consumed to create the final output value that your export is supposed to generate.
Note that here you're now going to create a "reactor" unlike in previous
excercises where a "command" was made instead. There's no
main
function to implement.
package example:demo world demo { // This interface, defined below, can be imported into your component. This // is used to determine how to implement the export below. import greeter-candidates // This is what you need to implement. export greeter } interface greeter-candidates { // Calling this function, which is imported into the component, will return // a list of candidates which needs to be handled to determine the return // value of the exported `greet` function. get: func() -> list<candidate> variant candidate { // This person is a hermit, they prefer to be left alone, don't greet them. hermit(person), // This person is excited, please greet them. excited(person), } record person { // This person's name name: string, // You're only interested in greeting lego enthusiasts, so require this // to be present and greater than zero. lego-count: option<u32>, } } interface greeter { // You'll implement this function. greet: func() -> result<greeting, error> record greeting { // Manufacture "Hello, $name!" given who you're greeting. saying: string, // how many legos they claim to have lego-count: u32, } type error = string }
Here's a component. That component exports a function which returns a name. The objective of this exercise is to embed that component in your own via composition. This exercise will look a lot like "Hello, $name!" from before except that the name-to-print is inferred from the component rather than the environment.
The greeting should be printed to stdout like before. This time though there's nothing in the environment that's extra to import.
package example:demo world demo { // ... nothing extra for this exercise }
Here's a component. That If executed it prints "Sorry, I'm not allowed to print the answer". Can you trick it into printing the answer?
The WIT world
for this exercise includes nothing extra.
Perhaps a good starting point is to take the component above and submit
it here to see what happens?
wasm-tools component wit
can be used to show
the WIT interface of a preexisting component. Running that on the
component provided above should yield:
package root:component world root { import wasi:io/streams import wasi:cli/environment import wasi:cli/stdout export wasi:cli/run }This component is internally making a decision to not print the answer, so how might it be using these imports/exports to make such a decision?
wasi:cli/environment
and provides a custom
environment to the component.
package example:demo world demo { // ... nothing extra for this exercise }