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

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:

(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.

Yay! That looks a lot nicer.

Published by Avdi Grimm

7 Comments

  1. Nice! I would just drop the extra ‘return’ usage in the Ruby version to make it a bit more idiomatic.

    Reply
  2. 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

      yourself
          ^self

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

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

    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
    p StringIO.new.instance_eval{
    print(x)
    print(‘ @ ‘)
    print(y)
    self
    }.string

    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.

    Reply
  3. 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.

    Reply
  4. 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]

    Reply
  5. Thanks Rick… I was coming in here specifically to say what you said about the receiver. 

    Reply

Leave a Reply

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