With BPatterns, you don’t need a special syntax to search or rewrite code. Any block or any AST node can become a matching pattern.
This opens up interesting opportunities to simplify existing users of the rewrite engine. One particularly good candidate is the deprecation mechanism in Pharo.
Let’s look at a real example.
Deprecations in Pharo Today
Consider this recently introduced deprecation in Object:
Object>>confirm: queryString self deprecated: 'Use the ConfirmationRequest instead.' transformWith: '`@rcv confirm: `@arg' -> 'ConfirmationRequest signal: `@arg'. ^ConfirmationRequest signal: queryStringThis method will be removed in an upcoming Pharo version. Users are expected to migrate from #confirm: to raising a ConfirmationRequest.
The interesting part is the transformation rule: any sender of #confirm: is automatically rewritten on the fly using the provided rewrite pattern.
Run your test suite with good coverage, and your codebase is upgraded automatically. No manual work required. That’s powerful.
But Let’s Look Closer
Even in this relatively simple example, a lot is duplicated:
- The deprecated message is duplicated twice in the transformation rule and the actual method name:
- '`@rcv confirm: `@arg'
- The replacement API appears three times:
- in deprecation description:
- 'Use the ConfirmationRequest instead'
- in the transformation rule:
- 'ConfirmationRequest signal: `@arg'
- at the end of the method to do the real API call:
- ^ConfirmationRequest signal: queryString
- The transformation rule duplicates symbolic pattern variables:
- `@arg
Personally, I always start by searching for an existing example to copy. I never try to write it cold.
There is a “Deprecate Method” refactoring in Pharo, but today it is very basic and does not help with transformations. It could be improved—but that’s where we currently are.
Now let’s look at the same idea through the lens of BPatterns.
The Same Rewrite with BPatterns
The core transformation rule can be written as a plain block:
[[ anyRcv confirm: anyArg ] -> [ ConfirmationRequest signal: anyArg ]] brewrite.
No strings. No backticks. No special syntax.
Now imagine embedding this rewrite directly into the deprecated method:
Object>>confirm: queryString | brewrite anyRcv anyArg newAST | brewrite := [[ anyRcv confirm: anyArg ] -> [ ConfirmationRequest signal: anyArg ]] brewrite.
"some code to actually rewrite the sender" newAST := brewrite rewriteMethod: thisContext sender home method. thisContext sender home receiver class compile: newAST formattedCode.
"And call new API at the end" ^ConfirmationRequest signal: queryStringThis already removes the string-based rewrite syntax, but we can do better.
In BPatterns, variables starting with any are wildcards by default—but you can explicitly configure any variable, including self and method arguments.
That means we can reuse the actual method variables directly in the rewrite rule:
Object>>confirm: queryString | brewrite newAST | brewrite := [[ self confirm: queryString ] -> [ ConfirmationRequest signal: queryString ]] brewrite with: [ self. queryString ].
"some code to actually rewrite the sender" newAST := brewrite rewriteMethod: thisContext sender home method. thisContext sender home receiver class compile: newAST formattedCode.
"And call new API at the end" ^ConfirmationRequest signal: queryStringNow the transformation rule and the method body speak the same language. And we can easily refactor it like any other method to avoid the duplication of the new API call:
Object>>confirm: queryString | brewrite newAST | newAPI := [ ConfirmationRequest signal: queryString ]. brewrite := [[ self confirm: queryString ] -> newAPI ] brewrite with: [ self. queryString ].
"some code to actually rewrite the sender" newAST := brewrite rewriteMethod: thisContext sender home method. thisContext sender home receiver class compile: newAST formattedCode.
^ newAPI valueAt this point, most of the code above is mechanical. BPatterns only needs an AST of the deprecated API call for the matching pattern.
Normally it comes from a block - [ self confirm: queryString ] - but here it can be easily derived from thisContext method ast since it is equivalent to the deprecated method header.
That means the whole deprecation can collapse into this:
Object>>confirm: queryString ^ self deprecatedBy: [ ConfirmationRequest signal: queryString ]That’s it.
No duplicated selectors.
No duplicated replacement calls.
No rewrite strings.
No boilerplate.
This is the deprecation without stress.
Variations Are Easy
Once the transformation is automatic, it’s natural to allow variations:
Disable auto-transformation:
Object>>confirm: queryString ^self deprecatedBy: [ ConfirmationRequest signal: queryString ] autoTransform: falseAdd a custom deprecation message:
Object>>confirm: queryString ^ self deprecated: 'self confirm: is a bad style , use ConfirmationRequest' by: [ ConfirmationRequest signal: queryString ] The basic version generates a default title automatically.
Summary
With the new API, the deprecation becomes a seamless, fluent process. You no longer need to think about transformation rules at all—they are simply part of the system, enabled by default. Deprecation is expressed in the same language as the code itself, not in a separate, string-based mini-language.
Once this foundation is in place, building higher-level tooling becomes straightforward. Wrapping deprecations into proper refactoring commands is trivial, and integrating them into existing refactorings—such as Rename Method or Add/Remove Argument—becomes a natural next step.
That’s all for now.
You can load BPatterns from GitHub and try new deprecations yourself:






