Exploring Unoptimized whileTrue: Loops in Pharo

A way to express loops in Pharo is through a while loop:

[ "condition" ] whileTrue: [ "logic" ]
  

Like with all the rest of the boolean logic operators, this too is implemented as a regular method in BlockClosure>>#whileTrue: whileTrue: aBlock "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the argument, aBlock, as long as the value of the receiver is true." self value ifTrue: [ aBlock value. self whileTrue: aBlock ]. ^ nil . The implementation is rather peculiar as it relies on recursion. The good news is that in most cases, as the comment of the method says, the call is inlined by the compiler. However, this inlining only happens when both the condition and the logic are explicit blocks. If either the condition or the logic blocks are expressed through variables instead, no inlining occurs. This, of course, can cause performance issues.

It's useful to keep an eye on these places. We can do that through a query like this:

'`{:node | node isBlock not} whileTrue: `@b' gtASTMatches |
'`@a whileTrue: `{:node | node isBlock not}' gtASTMatches
  

The query looks for two abstract syntax tree patterns: one in which the receiver of whileTrue: is not a block, and on in which the argument is not a block.

But, wait. We looked for these methods just because a comment in BlockClosure>>#whileTrue: whileTrue: aBlock "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the argument, aBlock, as long as the value of the receiver is true." self value ifTrue: [ aBlock value. self whileTrue: aBlock ]. ^ nil said so. How do we know when a call is inlined or not? By inspecting the method.

For example, if we inspect the method from the example above, one of the views shows us the bytecode of the method. We notice a bytecode described as send whileTrue: that corresponds to a message send.

Compare the above with a method as the one below in which both the receiver and the argument are blocks. The bytecode looks quite different. Instead of send whileTrue: we have the logic explicitly listed in the bytecode of the method. Notice how the one but last bytecode is a jump to the beggining of the loop.

What can learn from this experiment? An obvious thing to observe is that the system is explorable. Indeed, it is the nature of the Smalltalk environment to make everything uniformly accessible. But, that accessibility was doubled by views and interactions that provided insights into the objects we looked at. The moldable environment empowered us to combine different tools, like a query engine, a playground, inspector and method coder, to address higher level questions. Accessibility was transformed into explainability. This is the nature of a moldable development environment.