Question

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...

Was it helpful?

Solution

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 ;)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top