Sections
Today we are bringing together selectors as blocks from the last post and currying discussed before, to produce something pretty neat: sections.
We begin with an example. Using asBlock
from the last post, we can write
#> asBlock
to mean a block comparing two arguments. If we curry it with a number, say 42:
#> asBlock curried value: 42
we get a one-argument block which, when invoked, tells whether 42 is greater than the argument. We could use it as a regular one-argument block, for example, with select:
#(1 20 50 43 11) select: (#> asBlock curried value: 42)
Of course, this by itself isn't particularly exciting. [:each | 42 > each]
would have done the same, and without excessive mental acrobatics.
Without being too concerned about the form for now, let's consider what we have just done by writing that expression. We took an operator (a binary selector) and produced a function (a block) which is an application of that selector to 42 on the left and the function argument on the right. In Haskell such a construct is called a left section of an operator. Similarly, a block applying #> with 42 on the right and the argument on the left would be a right section. This is an interesting concept---apart from the unwieldy shape it had in our code. Let's fix that.
We add two methods to the system, one to the Symbol class, the other to Object.
Object>>~ mustBeSymbol | block | block := mustBeSymbol asBlock. block numArgs ~= 2 ifTrue: [self error: 'invalid selector']. ^block curried value: self Symbol>>~ anObject | block | block := self asBlock. block numArgs ~= 2 ifTrue: [self error: 'invalid selector']. ^[:a :b | block value: b value: a] curried value: anObject
This defines ~
as a section operator. Sent to a symbol, it produces a right section, sent to anything else with a symbol as the argument--
a left section. We can now rewrite the example as
#(1 20 50 43 11) select: #> ~ 42
or to select the opposite
#(1 20 50 43 11) select: 42 ~ #>
This is more interesting than just currying (and can if fact be rewritten to not rely on currying). It will work for any binary or one-argument keyword message:
Transcript ~ #show:
is a block that writes its argument to the transcript.
#print: ~ foo
produces a block writing the printString of whatever was in the variable foo to the argument, which must be a stream-like object.
#, ~ ', hello!'
is a block that appends ‘, hello!’ to the argument, and
'Hello ' ~ #,
prepends ‘Hello ‘ to it. In general, we can think of a tilda as “stick the selector and the object together, and the object missing for this to be a complete message send will be provided when the block we create is called”.