Function objects in Smalltalk (or executing blocks with no `value:`)
-
29-09-2019 - |
Question
Is it possible to send an anonymous message to an object? I want to compose three objects like this (think FP):
" find inner product "
reduce + (applyToAll * (transpose #(1 2 3) #(4 5 6)))
where reduce
, applyToAll
and transpose
are objects and +
, *
and the two arrays are arguments passed to anonymous messages sent to those objects. Is it possible to achieve the same using blocks? (but no explicit usage of value:
).
Solution
Perhaps what you really want to do is define a DSL inside Smalltalk?
OTHER TIPS
aRealObject reduceMethod: +;
applyToAll: *;
transpose: #(#(1 2 3) #(4 5 6));
evaluate
would work when aRealObject has defined the right methods. Where do you need a block?
You are looking for doesNotUnderstand:
. If reduce
is an object that does not implement +
but you send it anyway, then instead its doesNotUnderstand:
method will be invoked. Normally it just raises an error. But you can override the default, and access the selector +
and the other argument and do whatever you like with them.
For simplicity, create a class Reduce
. On its class side, define the method:
doesNotUnderstand: aMessage
^aMessage argument reduce: aMessage selector
Then you can use it like this:
Reduce + (#(1 2 3) * #(4 5 6))
which in a Squeak workspace answers 32, as expected.
It works because *
is already implemented for Collections with suitable semantics.
Alternatively, add a class ApplyToAll
with this class-side method:
doesNotUnderstand: aMessage
^aMessage argument collect: [:e | e reduce: aMessage selector]
and also add this method to SequenceableCollection
:
transposed
^self first withIndexCollect: [:c :i | self collect: [:r | r at: i]]
Then you can write
Reduce + (ApplyToAll * #((1 2 3) #(4 5 6)) transposed)
which is pretty close to your original idea.