Sahib Singh
Sahib Singh

Reputation: 59

How is Small talk's message to:do: implemented behind the scenes?

result := String new. 
1 to: 10 do: [:n | result := result, n printString, ’ ’]. 

Everything in smalltalk is an object and the objects interact through messages.

I couldn't understand how the above code is understanding the message to:do:

How is it able to iterate the block from 1 to 10? How does it know it has to repeat the block that many number of times?

Can someone explain what happens under the hood?

Upvotes: 3

Views: 199

Answers (2)

Leandro Caniglia
Leandro Caniglia

Reputation: 14858

Consider the method

m
  1 to: 10 do: [:i | self doSomethingWith: i]

Here are the bytecodes that Pharo generates

    pushConstant: 1                   ; i := 1
    popIntoTemp: 0                    ;
@2: pushTemp: 0                       ; i <= 10 ?
    pushConstant: 10                  ;
    send #'<='                        ;
    jumpFalse: @1                     ; if false, go to end
    self                              ; self doSomethingWith: i
    pushTemp: 0                       ;
    send #doSomethingWith:            ;
    pop                               ; 
    pushTemp: 0                       ; i := i + 1
    pushConstant: 1                   ;
    send #'+'                         ;
    popIntoTemp: 0                    ;
    jumpTo: @2                        ; loop
@1: returnSelf

As you can see the message #to:do is never sent, while #'<=' and #+ are (even though they are not in the source code!). Why? Because of what Bert said in his answer: these messages are optimized by the Smalltalk compiler. In the case of Pharo the optimization happens in #to:do:. In other dialects #to:do: is implemented in terms of #whileTrue:, which is the one that gets optimized.

Once you have understood how this works under the hood, get back to think of it as if #to:do: were a regular message with receiver 1 and arguments 10 and the block [:i | self doSomethingWith: i]. The optimization shouldn't obscure the semantics that your mind needs to follow.

Upvotes: 1

codefrau
codefrau

Reputation: 4623

All Smalltalk messages follow the pattern <receiver> <message>.. In this case the receiver is 1 (a subinstance of Number), and the message is to:do:.

You can browse class Number and see the implementation of to:do: right there:

to: stop do: aBlock | nextValue | nextValue := self. [nextValue <= stop] whileTrue: [aBlock value: nextValue. nextValue := nextValue + 1]

In your example, stop is 10 and aBlock is [:n | result := result, n printString, ’ ’]. So indeed, it is sending value: to aBlock repeatedly.

Now, in addition to that, many Smalltalks generate special byte code when they see the for:to: message, but this is just an optimization.

Upvotes: 8

Related Questions