SBPP #4: method Cascades

In Smalltalk, you can “cascade” side-effectful calls to the same object using the semicolon (;) operator. E.g.:

[gist id=”1238072″]

If I understand it correctly, the semicolon is effectively a K-combinator or “Kestrel”.

I am jealous. Sure, we have Object#tap, but that’s awfully verbose by comparison:

[gist id=”1238083″]

(I’m using multiple taps in order to exactly mimic the semantics of the Smalltalk semicolon operator in the example above.)

Let’s see if we can do better for this simple case of cascading a series of commands, while ignoring their return values.

[gist id=”1238090″ bump=1]

Yay! That looks a lot nicer.


  1. This isn’t exactly the semantics of Smalltalk’s cascade though.

    The semantics of
        rcvr msg1;msg2

    is the same as

       rcvr msg1
       rcvr msg2

    and the result is the value returned by rcvr msg2

    It’s for this reason that theres’ a method in Object


    which is used idiomatically in cases like intialiizing a new object:

         realWivesOfBedrock := Dictionary new;
             at: “Fred’ put: ‘Wilma’;
             at:  ‘Barney’ put: “Betty’;

    For a ruby built-in near equivalent to cascading, we might consider instance_eval which sets self in the evaluated block:

    require ‘stringio’
    x = 2; y = 42
    print(‘ @ ‘)

    which produces:

    “2 @ 42”

    One minor annoyance here is if we want to invoke assignment methods (e.g. an attr_writer generated method) we need to explicitly use self as a receiver in the block to distinguish between a block temporary variable and a method call.

  2. Sorry, but I can’t seem to control the indentation in my comment the way I want it.  I hope that the point comes across.

  3. Some more comments on the semantics of cascade.

    The canonical ‘Blue Book’ implementation/definition of Smalltalk is based on a stack oriented byte code virtual machine

    So a single method send

    aStream    print: xmight be compiled to something like the following (simplified) byte codes:  push aStream # push the receiver – stack is: aStream push x # push the argument – stack is: x aStream send #’print:’ # send the message – stack is now: [result of the message]The send pops the message selector, any arguments and the receiver from the top of the stack and replaces them with the result.The cascade would compile to something like:

    aStream    print: x;    nextPutOn: ‘ @ ‘;    print: y push aStream # stack is: aStream dup # stack is: aStream aStream push x # stack is: x aStream aStream send #’print:’ # stack is: [result] aStream pop # discard the result of the first message # stack is: aStream dup # stack is: aStream aStream push ‘ @ ‘  # stack is: ‘ @ ‘ aStream aStream send #’nextPutOn:’ # stack is: [result] aStream pop # stack is: aStream push y # stack is: y aStream send #’print:’ # stack is: [result of last message]

Leave a Reply

Your email address will not be published. Required fields are marked *