Search code examples
smalltalk

Chaining keyword messages


Let's suppose that I have an object, x, which can accept any one of the selectors, s1, s2, ..., sn. Let's further suppose that the result of any one of these selectors operating on the object is an updated object of the same type. I could then "chain" these selectors (as long as they are unary messages) as I wish, such as:

x s1 s2 ... sn

This will take the result of x s1 and apply selector s2, then apply selector s3 to that result, and so on. I would want to apply one or more of these selectors in some order for various results:

x s8 s2

In Smalltalk I can do this if the selectors are unary messages. However, if my selectors are keyword messages, I can no longer do this. If x individually accepts selectors, s1:, s2:, ..., sn:, then the following doesn't work:

x s1: a1 s2: a2 ... sn: an

There is the ; operator:

x s1: a1 ; s2: a2 ; ... ; sn: an

But using the cascading: each stage modifies the original x along the way, and I do not wish to modify x in this case.

To chain keyword messages, I think I'm left using the following syntax with parentheses:

(...(((x s1: a1) s2: a2) ... sn: an)

Which makes me feel like I'm programming in LISP if I have 3 or more keywrod messages. A specific example of this could be a multidimensional array. If foo is a 3 dimensional array, and you wanted to access an object at location 2,3,5 in the array, I think it would look like:

(((foo at: 2) at: 3) at: 5) some_object_selectors

That's a trivial example, of course, but illustrates the case. One might have other kinds of embedded objects, or other chain of consecutive object operations where you are interested in the end result.

Is there a more syntactically appealing way to do this in Smalltalk? I'm supposing there isn't another operator, perhaps a cousin to the ; operator (say we use & for example), which would chain them, such as:

x s1: a1 & s2: a2 & ... & sn: an

Since I would like to apply the selectors in any, or nearly any, desired order (for possibly different results), the selector form, s1:s2:s3:... is too confining. Also, this gives a facility which already exists in other languages, such as Ruby, where it would be equivalently expressed as:

x.s1(a1).s2(a2)...sn(an)

Lacking a special operator, an alternative could be to pass an array of selector-argument pairs, or perhaps a lookup table of selector-argument pairs. The lookup table requires setup for passing literals (it has to be created and populated), which makes me lean toward an array since I can simply write it as:

x { {s1. a1}. {s2. a2}. ... {sn. an} }

This is still a bit clunky, and I'm not so sure this is any more elegant than just using all the parentheses. I'm afraid my question may be at least partly subjective, but I am interested knowing what the best practice might be, and whether an operator exists that I'm not aware of which may help, or whether one is being entertained by a Smalltalk standards body.


Solution

  • Normally we give this object x a subject to operate on, and then ask for this changed subject when we're finished with it. Copying is rather inefficient.

    Object subclass: #Foo
        instanceVariableNames: 'subject'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Try'
    
    order: y
        self subject order: y
    
    select: z
        self subject select: z
    
    subject
        ^subject ifNil: [ subject := FooSubject new ]
    
    where: x
        self subject where: x