spawn! : Input(msg), (() => msg) => { }
Start a task. Its message arrives on a later Input.messages, in the
order the tasks finished.
The first argument is the App.Input that update! was handed. It is
never read: it is the witness that pins the closure's return type to the
app's own Msg. Without it msg stays free at the call site, the
closure compiles at whatever type its body alone implies -- often a
single-tag union with no discriminant -- while the host decodes the
result as the app's real Msg, producing the wrong tag or a misread
payload. Only the platform's entry module can name the requires bound
Msg, so an App.Input is how every other module names it. Pass the
input the callback already has; there is nothing to construct.
A task closure may capture the input, so a task can spawn more tasks.
When the task starts is the host's choice. It may run up to its first
waiting effect before spawn! returns, or in the host's turn after
update! returns; either way it has reached its first wait, or finished,
before render! of the same cycle. Its code and its synchronous effects
can therefore interleave with the rest of the update! that spawned it,
so do not assume an order between the two. The only order a task promises
is its message's: on a later cycle, after every task that finished first.
When the same kind of work can be in flight more than once, a reply can
arrive after a newer one. Put a generation counter or id in the message
and drop replies that do not match the latest; examples/http_fetch
shows the shape.
This is the only way to start a task. Input is a pure value declared in
the roc-ray-types package and has no effectful receivers, so there is no
input.spawn! form.
Legal in update! and in tasks; refused in init! and render!. init!
never sees the answering input, and render! does not change the world.