Wednesday, 7 February 2007

anObject select: #aMethodName considered harmful

I was browsing some of the Magritte source code, when I saw a message that confused me:
       selectors := anObject class allSelectors
select: #isDescriptionSelector.
I couldn't understand how you could pass a Symbol to #select: instead of a block-- certainly the definition of Collection>>select: implies it should only take a block. But trying it for myself, I saw it worked-- eg
{ 1. 2. 3. 4. } select: #even.
can be used in place of:
{ 1. 2. 3. 4. } select: [ :each | each even ]
Given how much simpler this looks, I was surprised I'd not seen this usage before, and searching for senders of #select: shows that the wordier version seems to be generally preferred. So what's this all about?

A posted a question to the Squeak-Beginners mailing list, and very quickly got some good feedback. Ron Teitelbaum gave a full explanation:
It's a trick. It only works because #value: is implemented on symbol as:
Symbol>>value: anObject
^anObject perform: self.
So the following works too.
#asUppercase value: 'I am a trick'
My suggestion is that you shouldn't follow such tricks. Although I do it too sometimes with things like:
aDictionary at: #foo ifAbsent: nil.
Since nil responds self to #value it seems redundant to me to put the nil in a block so that it can return nil when the block is evaluated with #value. Even though in this case the execution is faster but in the case of sending a symbol to select it is slower.

In both cases it is harder to read so in my opinion it is best to stick with arguments that are expected.
Lukas Renggli-- the author and maintainer of Magritte-- also responded, echoing Ron's comments, and adding that the latest versions of Magritte no longer use this idiom, so this is possibly the first and last place you will ever see this idiom used!

No comments: