Twitter icon.
Random notes on software, programming and languages.
By Adrian Kuhn

Pharo Superpower: Send Any Message

In Pharo Smalltalk you may send any message even if it’s name is not known at compile time. Sending any message is one of the superpowers that can be used for the good, even when doing application programming, therefore I will discuss best practices in the end.

First of all, recall that “sending a message” is Smalltalk jargon for calling a method. Since sending a message is synchronous in Smalltalk, ie it blocks until the receiver returns, it is basically the same as a method call and the actual difference thus of philosophical nature only. (There is an implementation difference deep down at the language’s core, but that shall not be discussed today as it does not matter to programmers.)

BTW, this is the fourth post in the Superpower series.

There are many ways to send any message, mainly due to optional and variable arguments which are not well supported by Smalltalk syntax. The most basic form is

object perform: #symbol withArguments: anArray

Let’s consider a real example

string = 'Lorem'.
string perform: #size. "=> 5"
string perform: #at: with: 1. "=> $L"
string perform: #copyFrom:to: with: 2 with: 4. "=> 'ore'"

If the number of arguments is not known at compile time, we may use

string perform: aSymbol withArguments: anArray.
string perform: aSymbol withEnoughArguments: anArray.

the first expects that the array matches the arity (number of arguments) of the target method, the latter will just use as many arguments are required. This is most useful to send a message with optional arguments.

So when is sending any message for the good or for the evil?

Whenever possible, try to avoid using #perform: because it is less readable. When a reader of your program looks at the a #perform: it is not obvious which message is being sent at runtime. Also, messages that are sent with #perform: will not be shown when browsing all senders of a message. There is one subtle difference here: If the dynamically sent message is stored somewhere as symbols, at least that symbols will show up when looking for senders. If however, the dynamically sent message is composed using string concatenation, it wont show up at all. It might even seem as if its implementers are never used, which can be very confusing to the reader.

For all above reasons you should only use #perform: when you have good reason to do so. And if you use it, make sure that the dynamically sent messages are all stored as a symbol somewhere else. Best of all, make sure that all code involved into dynamically sending message is encapsulated by one single class.

I will provide an example from Hapax’s clustering algorithm. When you do hierarchical clustering, there are different ways to link small clusters into large clusters. The call to this linkage method is buried deep down in the internals of the clustering algorithm, so the ClusteringEngine class uses a strategy pattern to pick the right linkage method. The choice of strategy is stored as a symbol in an instance variable and then used as follows

Object subclass: #ClusteringEngine
    instanceVariableNames: 'distanceMatrix dendrogram linkage'
    classVariableNames: ''

ClusteringEngine >> linkage: aSelector
    linkage := aSelector

ClusteringEngine >> linkage
    ^ linkage

ClusteringEngine >> allLinkageSelectors
    ^ #( averageLinkage centroid completeLinkage meanLinkage singleLinkeage wardsMethod )

ClusteringEngine >> run
    (distanceMatrix size - 1) timesRepeat:
        [self findMinimum.
        self perform: linkage].

ClusteringEngine >> averageLinkage
    "implementation omitted..."

"et cetera..."

All code is encapsulated with one class such that a reader can find it all in one place when browsing the source code.

Any other use of #perform:, in particular when string concatenation of selectors is involved, is evil and should be limited to library design, if used at all.

A note regarding performance. Using #perform: is as fast as sending a message the normal way. So contrary to popular believe there is not performance penalty—at least not in Pharo Smalltalk, in other dialects that do use JIT compilation there might be severe performance penalties though).

Hackety hacking!

6 Responses to “Pharo Superpower: Send Any Message”

  1. Carl Gundel Says:

    Great post.

    Sending a message is not calling a method as I have come to understand it. The reason it is called sending a message is because the receiver of the message gets to decide what to do, so it is not the direct invokation of a method. Without that indirection then yes it would be equivalent to a C function call. It doesn’t need to be asynchronous to be considered a message send.

  2. akuhn Says:

    You are right, this is the difference at the language’s core that I mentioned.

    When sending messages, the sender only encodes the name of message and the receiver decided at runtime which method is to be activated. When calling method, the caller encodes at compile time the address (or at least offset) of the target method, to which it directly jumps at runtime. However, from the programmer’s view the observed behavior is (typically) the same, both cases are synchronous and thus block until the “called” method returns. I mention this because other languages (or networking systems) have true message sending where control immediately returns to the “caller” and the called method is run in parallel, ie asynchronously. So Smalltalk is kind of a hybrid in that sense. (And if you delve into the land of JIT optimization, things are put upside down anyway, and we are back at calling or even inlining methods… :)

  3. Carl Gundel Says:

    Yes, of course as you ultimately get closer to the metal you dispense with dynamic code somewhere. It happens all over again when the CPU executes microcode. Down the rabbit hole we go! ;-)

  4. Travis Griggs Says:

    You inspired me

  5. akuhn Says:

    Just read it 5 minutes ago :) you do a way better job of picking up the superheroes theme in the text, awesome. Looking forward to more superfeats!

  6. For.example » Pharo Superpower: Respond to any Message Says:

    [...] Random notes on software, programming and languages.By Adrian Kuhn Pharo Superpower: Send Any Message [...]

For.example is Digg proof thanks to caching by WP Super Cache!