📜 ⬆️ ⬇️

Io Language: Message System

Today we will continue the series of articles begun by the honorable semka. Let's talk about messages.

There are no function calls in Io, but there is a message sending. A message may have arguments (almost like function arguments), but message arguments are not executed before being sent.


How messages are sent

Sending a message looks like “object”, “space”, “message”:
Database connect

The message may contain arguments:
Database findByName("Oleg")

The result of sending a message can also be sent a message:
Database findByName("Oleg") lastName # , "Andreev"

In other words, the abcd construction is equivalent to a().b().c().d() in some java-like syntax.
')

How messages are performed

First, remember that message arguments are not executed. If Database connect(blah-blah) was written, then blah-blah will not be executed before sending connect, but will be transmitted as part of the "connect (blah-blah)" message (yes, in words).

When an object receives a message, it searches for a slot with the name of this message (in our example connect ). If the slot is not found (as it usually happens), its search is performed recursively in all object prototypes and their prototypes. If the required slot is not found anywhere, then the search for the slot forward started (analogous to method_missing in Ruby). As soon as any suitable slot is found, its value is activated (activation). (Note: the object in which the found slot lies in will tell you the Object contextWithSlot (slotName).)

Activation

For normal values, activation does nothing, just returns that value. Thus, the activation of regular slots that store numbers, strings, or many other objects is no different from getting a value via getSlot ( slotName ). But those objects that are marked as activatable , call the activate method. By default, only two objects are activated: Block (blocks and methods) and CFunction (links to syshny functions). All other objects can be made activated with setIsActivatable (true).

Slot activation does not occur when the getSlot (slotName) method is called (of course, the “getSlot” slot itself is activated, but the slotName is no longer). Therefore, if you want to get a method or an as-is block without calling it, use getSlot (methodName).

With activation is associated with one pitfall, which will be discussed at the end of the article.

Uh And when are the arguments executed?

Message sent, slot found and activated. But when and in what context will the message arguments be calculated?

Consider an example:
withoutArgs := method() # nil
withoutArgs("Hello!" println)

This code will not output anything because the argument "Hello!" Println was not executed.
withArgs := method(a, b, c, list(a,b,c)) #
withArgs("Hello!" println) # list("Hello!", nil, nil)

This code will execute the first argument and display Hello !. Missing arguments will be nil.

One more example:
withTwoArgs := method(a, b, nil)
withTwoArgs(1 print, 2 print, 3 print, 4 print)

This code will print 12, but not 1234.

The most shrewd already understood that only the declared arguments are executed. The funny thing is how they are executed, where and where you can run your dirty hands. And why all this is necessary.

Introspection method call

Do you still remember that in Io there are only objects, prototypes, slots and messages? There are no global and local variables in this list.

When Io activates a slot (that is, calls a method), it creates a special Locals object. This is a pretty fun object with a number of important features. First, the prototype of this object is self, i.e. pointer to the object to which the message was sent. Thus, we can create local slots (aka local variables) without interfering with the recipient object, as well as get access to all slots of this object. Secondly, there are at least three slots in this object: self (points to the recipient object), call (contains a lot of information about the call), and updateSlot. The latter is different from the usual updateSlot in that it updates the slot not in the locals, but in the receiving object. This is done solely for the sake of convenience, to write a = b instead of self a = b.

The most remarkable thing about the call object is a few slots:

Source: https://habr.com/ru/post/29375/


All Articles