OK, I've been driving myself nuts trying to work out how Urbit does I/O when it's implemented using Nock and Nock doesn't do I/O.
It's now the middle of the night and I think I've got it.
Since it's not in the Nock spec, and the Nock spec is defined in terms of nouns, it can only be hidden in the implementation of a noun.
A naive reading of the spec suggests there are two kinds of noun:
- a literal value (arbitrary-size integer)
- a pair of nouns
The only way it can work is if there are at least four kinds of noun
- a literal value
- a pair of nouns L and R
- the stream of input events
- a nock invocation on a pair of nouns A and F
Further, the "opcode 2" reduction in the Nock evaluator is not implemented by recursing the Nock evaluator, but by returning a type 4 noun.
A type 3 noun "counts" as a pair, where L is the next event in the input stream and R is another type 3 noun
The runtime creates a type 4 noun where A is a type 3 noun and F is the system-implemented-in-nock
It then calls a native function output(n) on the noun it created.
output(n) looks at the type of n. If it's type 1, it treats it as an output event and "performs" it.
If it's type 2, it calls output on L, then on R
If it's type 4, it runs the Nock evaluator on it and calls output() on the result.
Can anyone who's looked into the vere source tell if that is about right?