What a value says about itself
In osen a function is not a special kind of thing. It is a bundle with a couple of named parts, and the parts have names you can read. This page is about those names: what they are, how to write one by hand, and why the language works this way.
You should have read LANGUAGE.md first — this assumes you can
already read (1, 2) /o/add.
The one rule
A bundle can say which of the machine’s registers it carries. Running it means installing them. A bundle that says nothing is data.
Everything below is that sentence with examples under it.
The important half is the last clause. A bundle you were handed — read off a socket, pulled out of a message, sent from another machine — does not do anything by being looked at. It runs when something puts it somewhere that runs things, and it can only be put there deliberately.
( ) and { }
Both group things. The difference is when.
(1, 2) # → ( : 1, : 2 )
{1, 2}
The second prints : { : 1, : 2 }: the braces are still on. ( ) means
evaluate this here. At the top level “here” is immediately, so
(1, 2) has already run and left you a bundle. { } means don’t — you get
the thing itself, unevaluated, to keep or pass on.
That is why arguments go in parentheses and a function body goes in braces:
(1, 2) /o/add # → : 3
The parenthesised part is evaluated, then handed to /o/add. If you wrote
the body of a function in parentheses it would run at the moment you defined
it, which is not what you meant.
Defining a function
/o/defn takes a body in braces and the names the body is waiting for:
/g : ({ (/a, /b) /o/add }, ["/a","/b"]) /o/defn,
(1, 2) /g
# → : 3
Being able to write it by hand is not a party trick. It is the reason a function can arrive over a network: there is nothing in it that only this machine understands.
Naming the parts
/o/defn also takes its arguments by name, in any order:
/g : ( /body : { (/a, /b) /o/add },
/args : ["/a","/b"] ) /o/defn,
(1, 2) /g
# → : 3
The positional form is shorthand for the first two. The named form exists because there are two more slots, and a positional list that grows breaks every reader of it.
/captured — fix a name where the function is written
By default a function looks names up where it runs:
/c : 10,
/g : ( /body : { /c } ) /o/defn,
/c : 99,
() /g
# → : 99
Declare the name and it is resolved and stored when the function is defined, so later changes do not reach inside it:
/c : 10,
/g : ( /body : { /c }, /captured : ["/c"] ) /o/defn,
/c : 99,
() /g
# → : 10
This is what makes a function portable: everything it needs travels with it.
An omitted list means empty, never “work it out” — so a function without
/captured captures nothing, and one that lists a name that is not bound
anywhere captures nothing and says nothing. Nothing is inferred on your
behalf.
/ambient — reserved
/ambient is accepted and does nothing an undeclared name does not do. It
is reserved for names a function expects to find wherever it is run. Writing
it is harmless; it changes nothing.
When a name is not bound
An undeclared name that resolves nowhere is not an error in the language. It is a question, and the host answers it. A command line reports it; a Max patch can send the name out an outlet and let the patch supply a value; a microcontroller host can ship the whole computation somewhere that does know the name.
So “not bound” is where a program asks, not only where it fails.
Finding out where a name came from
/foo : 7, ("/foo") /o/whence
# → : "/_e"
("/o/add") /o/whence
# → : "/_x"
("/nope") /o/whence
# → : ""
/_e is your environment, /_x the host register, a three-character
answer like /zz is a register, and "" means nowhere. It asks the same
question the machine asks when it resolves a name, so it cannot disagree with
it — and it never triggers the unbound behaviour above, so you can ask about
a name without setting anything in motion.
Implementation — for embedders and module authors
Implementation
What they turn into
A bundle can be an element of another bundle, or an item inside a message. Those are different places, and OSC only lets a bundle be the first one — so anything that lands in a message gets tagged to say what it is:
/a : (1,2), (/a) /o/typeof1 # → : "/-/|"
/b : {1,2}, (/b) /o/typeof1
# → : "/-/b"
| means “this message item holds a bundle, treat it as a sequence”; b is
an ordinary blob. Both are representations. Neither one means the value
will run. You will see | in printed output and it is worth recognising,
but you do not have to do anything about it.
The registers
The machine has five registers, and they have short names because they are plumbing rather than vocabulary:
/_i |
the input — work waiting to be done |
/_e |
the environment — what names mean here |
/_s |
the stack |
/_c |
the control |
/_d |
the dump |
A value that says /_i : <a bundle> is saying “this bundle is code, and
running me means making it the input.” That is all a function is.
A thunk
The smallest thing that says it is code:
/t : { /_i : { (1,2) /o/add } },
() /t
# → : 3
Read it outside in:
- the outer
{ }keeps the whole thing from running when you write it — it is the value you are holding; /_isays what it carries;- the inner
{ }is the body, and it has to be a bundle.
The inner braces are not decoration. Without them you have written an assignment whose value is an expression, not a declaration whose value is a body, and the call fails:
/t : { /_i : (1, 2) /o/add }, () /t
wrong type of item
Running one
Apply it with (), as above. /o/exec also works, on a literal or through
a name:
(( /_i : { (1,2) /o/add } )) /o/exec
# → ( : 3 )
/t : { /_i : { (1,2) /o/add } }, (/t) /o/exec
# → ( : 3 )
A bundle that declares nothing comes back untouched, which is the rule at the top doing its job:
/a : (1,2), (/a) /o/exec # → ( : ( : 1, : 2 ) )
/o/exec did not refuse and did not run it. There was nothing to install, so
you got your bundle back.
A hand-written function
Add parameters to a thunk and you have a function. /_p is the list of
names the body is waiting for:
/g : { /_i : { (/a, /b) /o/add }, /_p : ["/a","/b"] },
(1, 2) /g
# → : 3
That is a hand-written lambda, with no special syntax and no constructor.
It is an ordinary bundle with two ordinary addresses, and you can print it,
diff it, store it, or send it somewhere. /o/defn builds exactly the same
value.
Note the two vocabularies: you write /body and /args, and the value
carries /_i and /_p. That is deliberate — the short names are the
machine’s registers, and you should not have to know them to write a
function.
Why it is built this way
The machine’s whole state is an OSC bundle, so a running program and a message on a wire are the same bytes. That is only useful if a receiver can be handed those bytes and decide what to do — and it can only decide if the bytes say what they are.
Hence the rule at the top. A value that declares /_i is offering to be run.
A value that declares nothing is data, and stays data no matter who receives
it or where they put it. There is no tag, no convention and no out-of-band
agreement that turns one into the other; you read the addresses, which are
ordinary OSC addresses that you could have written yourself.
See also
- LANGUAGE.md — the guided tour
- GRAMMAR.md — the syntax rules, precisely
- CALLING-CONVENTION.md — what happens to values at a call
- REFERENCE.md — every
/o/*name