Tuesday, August 30, 2011

Point-free programming in Smalltalk

Smalltalk does not, ordinarily, support point-free programming. Point-free programming is programming without "points", or variables. In a loop, rather than assigning every item to loop over to a variable, you only write which function should be called on them.

Pharo Smalltalk has limited support for that. There, you can write:

#(1 2 3 4) select: #even -> #(2 4)

The above is implemented using an easy equivalence. Every block that sends only one message is equivalent to the selector of that message. Thus, the above is equivalent to:

#(1 2 3 4) select: [:e | e even] -> #(2 4)

Which is just standard Smalltalk. The equivalence can be made work by implementing #value: etc. on Symbol.

Well, of course this is rather limited, we still haven't got rid of blocks. We still need blocks for everything that does not only send one message. You can push a little farther by allowing composition of symbols:

{'100' . '20' . '3'} sort: #<= * #first. -> #('100' '20' '3')

The idea is to read the asterisk as "after", like the function compositor in Mathematics.

So you read this as "sort the area by less than, after first was applied". Thus, the strings are sorted by their first letter. Awesome, eh?

This is not standard Pharo, but it can be implemented in a couple of minutes. The code is on Snipt, but it's more easily written than read.

This can still not replace blocks. For example, the following block still cannot be expressed point-free:

[:a | Transcript show: a]

But nothing is easier than inventing a syntax for that (can you implement it in Pharo, so it'll work?):

 Transcript delayedSend: #show: 

Finally, for blocks where the left-hand-side is the argument:

 [:stream | stream nextPutAll: 'hello']

I propose that could be written as follows (can you implement it in Pharo, so it'll work?):

Delayed nextPutAll: 'hello'

Open questions

Would it be more enjoyable to write entirely point-free in Smalltalk? What do you think?