Clean Blocks in Pharo 14

MD
Marcus Denker
Wed, Jul 9, 2025 8:05 AM

We have enabled the "Clean Block Closure" option for Pharo14

What is this?

Until now, blocks are created at runtime, but there are blocks that actually do not need any runtime data to be created.

Thus "Clean blocks" are those blocks that
- do not access self
- do not access instance variables (which needs self)
- do not return
- do not access temp variables from outer blocks or the method

With the clean block option enabled, the compiler can create the object at compile time.

For Pharo12, we already enabled a sub-set: Constant Blocks. Now we do the next step: pre-create all clean blocks.

What is the effect?

Positive:
- much faster (we see a factor of 9 speedup for creating a block like [1+2] )
- the resulting block does not reference the outer context / self
=> GC can cleanup more
- #isClean check on blocks is much faster (nice if you check for it e.g. for serialization)

But:

This means that we now have blocks that have no outerContext and #receiver returns nil.
All code that relies on these needs to be changed to take this into account.

We enabled Clean Blocks early in Pharo14 so we find all remaining problems, especially related to external code.

So if you see strange behavior (especially in the debugger), please open an issue on the issue tracker!

We are interested, too, in cases where you rely on either the receiver or the outerContext for blocks in your own code.

What next?

  • Deprecate #receiver on BlockClosure
  • Track down all remaining problems (e.g. debugger)

After:
- we want to enable to option to only reference the outerContext only for those blocks that need it (that do a non-local return).
(there is a compiler option already, but disabled by default)
- only blocks that access self should hold onto the receiver
- .. and more

We have enabled the "Clean Block Closure" option for Pharo14 # What is this? Until now, blocks are created at runtime, but there are blocks that actually do not need any runtime data to be created. Thus "Clean blocks" are those blocks that - do not access self - do not access instance variables (which needs self) - do not return - do not access temp variables from outer blocks or the method With the clean block option enabled, the compiler can create the object at compile time. For Pharo12, we already enabled a sub-set: Constant Blocks. Now we do the next step: pre-create *all* clean blocks. # What is the effect? Positive: - much faster (we see a factor of 9 speedup for creating a block like [1+2] ) - the resulting block does not reference the outer context / self => GC can cleanup more - #isClean check on blocks is *much* faster (nice if you check for it e.g. for serialization) But: This means that we now have blocks that have no outerContext and #receiver returns nil. All code that relies on these needs to be changed to take this into account. We enabled Clean Blocks early in Pharo14 so we find all remaining problems, especially related to external code. So if you see strange behavior (especially in the debugger), please open an issue on the issue tracker! We are interested, too, in cases where you rely on either the receiver or the outerContext for blocks in your own code. # What next? - Deprecate #receiver on BlockClosure - Track down all remaining problems (e.g. debugger) After: - we want to enable to option to only reference the outerContext only for those blocks that need it (that do a non-local return). (there is a compiler option already, but disabled by default) - only blocks that access self should hold onto the receiver - .. and more
DS
Daniel Slomovits
Wed, Jul 9, 2025 7:37 PM

This is very cool to see. One note—I would personally not outright
deprecate #receiver, but leave a comment indicating that it is not
guaranteed to be set and why. There are plenty of cases where getting nil
as the receiver of a clean block is correct, or at least good enough—I
wouldn't want to eventually lose the ability to officially access it
entirely, which is what deprecation implies.

On Wed, Jul 9, 2025 at 4:06 AM Marcus Denker marcus.denker@inria.fr wrote:

We have enabled the "Clean Block Closure" option for Pharo14

What is this?

Until now, blocks are created at runtime, but there are blocks that
actually do not need any runtime data to be created.

Thus "Clean blocks" are those blocks that
- do not access self
- do not access instance variables (which needs self)
- do not return
- do not access temp variables from outer blocks or the method

With the clean block option enabled, the compiler can create the object at
compile time.

For Pharo12, we already enabled a sub-set: Constant Blocks. Now we do the
next step: pre-create all clean blocks.

What is the effect?

Positive:
- much faster (we see a factor of 9 speedup for creating a block
like [1+2] )
- the resulting block does not reference the outer context / self
=> GC can cleanup more
- #isClean check on blocks is much faster (nice if you check for
it e.g. for serialization)

But:

This means that we now have blocks that have no outerContext and #receiver
returns nil.
All code that relies on these needs to be changed to take this into
account.

We enabled Clean Blocks early in Pharo14 so we find all remaining
problems, especially related to external code.

So if you see strange behavior (especially in the debugger), please open
an issue on the issue tracker!

We are interested, too, in cases where you rely on either the receiver or
the outerContext for blocks in your own code.

What next?

  • Deprecate #receiver on BlockClosure
  • Track down all remaining problems (e.g. debugger)

After:
- we want to enable to option to only reference the outerContext
only for those blocks that need it (that do a non-local return).
(there is a compiler option already, but disabled by
default)
- only blocks that access self should hold onto the receiver
- .. and more

This is very cool to see. One note—I would personally not outright deprecate #receiver, but leave a comment indicating that it is not guaranteed to be set and why. There are plenty of cases where getting nil as the receiver of a clean block is correct, or at least good enough—I wouldn't want to eventually lose the ability to officially access it entirely, which is what deprecation implies. On Wed, Jul 9, 2025 at 4:06 AM Marcus Denker <marcus.denker@inria.fr> wrote: > We have enabled the "Clean Block Closure" option for Pharo14 > > # What is this? > > Until now, blocks are created at runtime, but there are blocks that > actually do not need any runtime data to be created. > > Thus "Clean blocks" are those blocks that > - do not access self > - do not access instance variables (which needs self) > - do not return > - do not access temp variables from outer blocks or the method > > With the clean block option enabled, the compiler can create the object at > compile time. > > For Pharo12, we already enabled a sub-set: Constant Blocks. Now we do the > next step: pre-create *all* clean blocks. > > # What is the effect? > > Positive: > - much faster (we see a factor of 9 speedup for creating a block > like [1+2] ) > - the resulting block does not reference the outer context / self > => GC can cleanup more > - #isClean check on blocks is *much* faster (nice if you check for > it e.g. for serialization) > > But: > > This means that we now have blocks that have no outerContext and #receiver > returns nil. > All code that relies on these needs to be changed to take this into > account. > > We enabled Clean Blocks early in Pharo14 so we find all remaining > problems, especially related to external code. > > So if you see strange behavior (especially in the debugger), please open > an issue on the issue tracker! > > We are interested, too, in cases where you rely on either the receiver or > the outerContext for blocks in your own code. > > > # What next? > > - Deprecate #receiver on BlockClosure > - Track down all remaining problems (e.g. debugger) > > After: > - we want to enable to option to only reference the outerContext > only for those blocks that need it (that do a non-local return). > (there is a compiler option already, but disabled by > default) > - only blocks that access self should hold onto the receiver > - .. and more > >
MD
Marcus Denker
Thu, Jul 10, 2025 8:34 AM

On 9 Jul 2025, at 21:37, Daniel Slomovits daniels220@gmail.com wrote:

This is very cool to see. One note—I would personally not outright deprecate #receiver, but leave a comment indicating that it is not guaranteed to be set and why. There are plenty of cases where getting nil as the receiver of a clean block is correct, or at least good enough—I wouldn't want to eventually lose the ability to officially access it entirely, which is what deprecation implies.

Yes, true… I wonder how to provide support for people to find potential problematic use.

One thing we really need is Code Critics, but not doing static analysis but working on test run data.

We could
-> add probe in #receiver
-> run tests
-> log all send sites
-> tool shows all uses of #receiver that need to be checked

Marcus
> On 9 Jul 2025, at 21:37, Daniel Slomovits <daniels220@gmail.com> wrote: > > This is very cool to see. One note—I would personally not outright deprecate #receiver, but leave a comment indicating that it is not guaranteed to be set and why. There are plenty of cases where getting nil as the receiver of a clean block is correct, or at least good enough—I wouldn't want to eventually lose the ability to officially access it entirely, which is what deprecation implies. Yes, true… I wonder how to provide support for people to find potential problematic use. One thing we really need is Code Critics, but not doing static analysis but working on test run data. We could -> add probe in #receiver -> run tests -> log all send sites -> tool shows all uses of #receiver that need to be checked Marcus