asoma

An anarchic society of machines.

Connecting machines is easy; getting them to behave as one is the work. Asoma is a small virtual machine and a language for it, built so that the boundary between machines matters less.

Anything with the machine in it — a laptop, a browser, a board, a Python process — offers what it can do as addresses, and what it does is a program: written anywhere, sent to it, composed from several machines at once, replaced without a compiler. A program is a bundle of OpenSoundControl (OSC) messages, and so is the running machine itself — its values, its names, the rest of its program — so a running program can be sent too, and carry on where it lands.

The language

Each box below holds a live machine, compiled to WebAssembly. Every line you write is a message to a local OSC server: the machine inside the box, on this page. Edit the program and press Run.

A message is an address and a value. Parentheses gather messages into a bundle, a colon binds a value to a name, and /o/get reads a message back out of a bundle by its address:

# a bundle of messages, bound to a name
/voice : (/freq : 440, /amp : 0.5),
/voice,
(/voice, "/freq") /o/get
# → : (
# →  /freq : 440,
# →  /amp : 0.5
# → )
# → : 440

Each line answers with a message of its own; the leading colon marks one with no address. Commas separate the lines of a program, and # starts a comment.

A call puts the arguments first and the function after them, as if the bundle of arguments were sent to the machine and then the function to apply to it:

/freq : 440,
# arguments first, then the function
/octave : (/freq, 2) /o/mul,
(/freq, /octave)
# → (
# →  : 440,
# →  : 880
# → )

A function is a body in curly braces — a bundle kept for later rather than run now — and the names of its parameters. /o/defn puts them together, and the result is called like anything built in:

# a body kept for later, and its parameter
/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
(/freq : 220, /octave : (/freq) /octave/up)
# → (
# →  /freq : 220,
# →  /octave : 440
# → )

That function is itself a bundle of messages: the body’s instructions and the parameter list. /o/inspect shows them:

/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
/octave/up,
# the function, as the messages it is made of
(/octave/up) /o/inspect
# → : <fn (/hz)>
# → : ": {
# →  /_i : {
# →   : [ "/!/os/g", "/$/hz", 2, "/o/mul", "/!/os/C2" ]
# →  },
# →  /_p : "/hz"
# → }"

And so is the machine’s own memory. />/_e puts the environment on the table: every name this program has bound, as one bundle. This is what would travel if the program moved to another machine. (The two empty bundles are the scopes the running line opened; how osen evaluates explains them.)

/freq : 440,
/octave/up : ({ (/hz, 2) /o/mul }, "/hz") /o/defn,
# the machine's memory: everything bound so far
/>/_e
# → (
# →  /octave/up : <fn (/hz)>,
# →  /freq : 440,
# →  (),
# →  ()
# → )

The guided tour of osen goes on from here — choosing, lists, folding, and how names are found.

Where it runs

The same machine runs in each of these. Any of them can send a program to any other but a browser to a browser, and the command line, the board and the browser can send each other a running machine.


Try it · Start · Overview · Hosts · Examples