Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell an object to execute a message

I have an object, and a string containing a message I would like to send to it.

For example, the string '+ 5', which I would like to send to some integer object.

If it was in the workspace then I would just write "obj + 5", but I need it to be done at running time without knowing the string in advance...

like image 498
Mugen Avatar asked Dec 17 '22 00:12

Mugen


1 Answers

If you can separate the argument of the message from the message itself, then you can "perform" the message send:

obj := 3.
msg := '+'.
arg := 5.
result := obj perform: msg asSymbol with: arg.

Otherwise, you would have to use the Compiler, which would translate the string into compiled code and execute it:

obj := 3.
msg := 'self + 5'.
result := Compiler evaluate: msg for: obj logged: false.

A common technique to avoid repeated compilation is to compile a block, which can be evaluated more efficiently:

obj := 3.
msg := '[:x | x + 5]'.
block := Compiler evaluate: msg.
result := block value: obj.

Btw, the code above (and below) is for Squeak, other Smalltalks may have a different way to access the Compiler.

There is an even more hackish way that lets you access the variable directly from the string. This is by executing the compiled code in "thisContext" (in this case you need to declare the temp vars even in a workspace):

| obj msg result |
obj := 3.
msg := 'obj + 5'.
result := Compiler new evaluate: msg in: thisContext to: nil.

However, I would not recommend this last technique. Performing is generally safer than involving the Compiler. That said, it can be used to implement some serious meta stuff. E.g.:

| obj |
obj := 3.
'The result is {obj + 5}.' expand

Implementation of the "expand" method is left to the curious reader ;)

like image 99
Vanessa Freudenberg Avatar answered Jan 07 '23 05:01

Vanessa Freudenberg