Io, messages, and message passing
This is a loose set of notes and thoughts on how Io works and how it relates to other things I'm interested in
— &
Io's process of executing code is centered around a tree of messages. Each node in this tree has two branches: next pointing the next part of the message, and arguements pointing to the list of inputs for that message.
The tree representing the message a(x, y) b(w c(z))
Raw messages have no context. It is a literal string of raw symbols (baring syntatic requirements of Io such as () and ,). Io follows the principle of "extreme late binding". The evaluation of message is lazy. Nothing is computed until a value is requested.
In order for a message to have meaning, they must be given context. One such way of providing this context is doMessage(aMessage, optionalContext).
doMessage(aMessage, optionalContext)Evaluates the message object in the context of the receiver. Returns the result. optionalContext can be used to specific the locals context in which the message is evaluated.
Since even the act of assigning values within an object is done with messages, it's possible to introudce syntatic exension to Io with little effort. For example, named arguments:
add(
x := 0 ; y := 0
doMessage(call argAt(0))
doMessage(call argAt(1))
x + y
)
add(x = 1, y = 2)
Context and Execution
The actual implementation details of how Io executs a message are hard to follow. Blocks hold a message plus its context while a message is at rest. Blocks can be activated which begins message processing. method and block are used to construct the Block object.
block captures the scope it was created, while method captures nothing. The scope of a method is always the target of a message. This context is used for resolving the look up of slots. Io goes through great work to build this conextual information.
Interpreting Message
At the very core of Io, is a simply while loop that walks over this tree of messages. Each message is dispatch to the current target. The result of that dispatch is then used as the next target. This continues until Io reachs a ; message. ; triggers the interpeter to reset the target back to the current scope.
As Rewriting
Io's message processing loop could be further examined under the lens of rewriting. A rewriting system emulating Io could be framed as three state objects (the target, scope, message) plus a rule set. The rule search mirrors message dispatch.
Rule search involves taking the state, and finding which rule matches some part of it. Message dispatch invovles taking a message, and finding the slot that accepts it. Thus, a rewriting system based around objects and method dispatch would merge these: Rule search involves taking the message, and finding which rule accept it.
In an OOP rule system, rules would write their changes back to their private state instead of the global shared state. This provides encapsulation as well as the potential for concurrent and parallel rewriting.
Self-Similarity
Since everything is objects in Io, the "tree of messages" is also a "tree of objects". Io's state spawns from it's root object: The Lobby. This means all state in Io also forms a tree. Furthermore, Io is prototype-based, its prototypes support multiple inheritance. When a message is looked up, Io navigates this tree of prototypes.
Code is a tree of objects. The state is a tree of objects. And the capability of objects is also a tree of objects. Everything is a tree of objects.
More advance techniques with prototypes, proxies, and weaklinks may provide the means to escape the tree and its opressive structure.