Fork me on GitHub

Component Model Exercises

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.

Hello, World!

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
}

Hello, $name!

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
}

{ Hello: "$name!" }

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
}

Composition Intro

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
}

Intercepting Imports

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?

Hint #1
The command 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?
Hint #2
If you upload your own component that prints out environment variables, does anything look interesting?
Hint #3
Using composition from the previous exercise try making a component which exports wasi:cli/environment and provides a custom environment to the component.
package example:demo

world demo {
  // ... nothing extra for this exercise
}

Console output