Async with channels, futures, and handlers

Handling messages directly

When users connect, the primary way they can interact with the document is to send messages to the document. This requires a message like:

public string output = "Hello World";

message ChangeOutput {
  string new_output;

This establishes the shape of the communication, and we leverage a channel to open a pathway for messages to execute code. One option is to add a message handler:

channel change_output(principal sender, ChangeOutput change) {
  output = change.new_output;

This enables users to send messages via the change_output channel which will execute the associated code. In this example, 'change_output' is the name of the channel which clients will annotate their message with to execute the associated code. Nothing stops you from introducing multiple channels with the same message type.

channel change_output(principal sender, ChangeOutput change) {
  output = change.new_output;
channel set_output(principal sender, ChangeOutput change) {
  output = change.new_output;

Waiting for users via futures

While accepting messages indiscriminately is great for applications such as chat, you need a way to combine multiple messages in an orderly fashion. Remember, Adama was designed for board games, and a good pattern to use is async/await pattern within code. Here, we define a message and an incomplete channel.

message SomeDecision {
  string text;

channel<SomeDecision> decision;

And any state machine transition block can then leverage the channel to pull data from a user.

private principal player1;
private principal player2;

#getsome {
  future<SomeDecision> sd1 = decision.fetch(player1);
  future<SomeDecision> sd2 = decision.fetch(player2);
  string text1 = sd1.await().text;
  string text2 = sd2.await().text;
  // do something with text1 and text2 to decide a winner, I guess?

Note, these two users can come up with the message independently and this decoupling of asking a person (i.e. the fetch) and getting the data in code (i.e. the await) enables concurrency.


channels have a fetch method that will result in a single message but has the semantics that the document can block forever. There is a mechanism called preempt which can unblock the state machine, but this is an experimental feature.

channel<T>.decide(who, T[])

Similar to fetch, the decide function enables something very cool and is the recommended approach for board games. Decide will ask the user to pick exactly one item from the given array of options. This is exciting as this enables AI to autoplay games, but this is outside the topic of the documentation... for now.