Hey Pedro,
On Fri, Sep 25, 2026, at 3:02 PM, Marco Pivetta wrote:
Hey Pedro,
On Fri, 25 Sept 2026 at 21:59, Pedro Veloso
<contatopedrohalves@gmail.com> wrote:class Ha { private int $a = 1; #[Pure] public function getA(): int { return $this->a; } } new Ha()->getA(); // Fatal error: pure method Ha::getA() accesses $this.Overall, I would also like to see functional purity in PHP, but a n
instance method is just a function with an implicit additional `$this`
argument, and it is pure if it causes no state mutation.Marco Pivetta
Marco "Ocramius" Pivetta (@ocramius@mastodon.social) - Mastodon
I am also a big fan of functional purity in most forms. However, as noted there's already many SA tools that support this. (Via @pure or #[Pure]. There's even an unreleased PER from FIG that has one.). So for this to make sense in the language itself, it needs to offer more benefit than just "the same thing but in the language."
The OP mentioned possible optimizations or inlining or things like that. At least some of those would need to be included initially to justify the RFC. Eg, #[Pure, Memoize] to mark it pure and auto-memoize the function, #[Pure, Inlineable] to allow the compiler/optimizer to compile away the function call, maybe the function call itself could be made cheaper if we know the function is pure... there's likely lots of options, but someone with more engine knowledge would need to say what's feasible. But we would need something.
The other catch is that, as Marco notes, purity is not always trivial to discover. Especially when passing around objects, if you call a method on the object, it MIGHT mutate something? Who knows? So defining precisely what we're able to enforce would be important, too.
--Larry Garfield
On 2026-09-26 09:48, Larry Garfield wrote:
The OP mentioned possible optimizations or inlining or things like that. At least some of those would need to be included initially to justify the RFC.
>
I'm also keen on explicit purity if there is some actual benefit (e.g. memoisation, inlining, partial evaluation) to making the assertion.
The other catch is that, as Marco notes, purity is not always trivial to discover. Especially when passing around objects, if you call a method on the object, it MIGHT mutate something? Who knows? So defining precisely what we're able to enforce would be important, too.
(As a simple example of not-pure-after-all, strval(1/7) is affected by the precision ini setting.)
If it is something that is being enforced then it's something that could potentially be checked during compilation anyway (as soon as an impure operation is encountered it poisons the function (and, transitively, any callers to the function)), and so the assertion wouldn't be needed.
Hi Larry,
At least some of those would need to be included initially to justify the RFC.
I’m not sure optimization needs to be the justification here. A
language-enforced purity contract could be useful on its own, and
inlining does not require purity.
PHP already has
[`zend_try_inline_call()`](php-src/Zend/Optimizer/optimize_func_calls.c at 267c6dc694aa9e5a4c76e5a2ee8938e5be2149d3 · php/php-src · GitHub),
which removes eligible calls to user functions that return constants,
without any attribute. It is limited, but expanding inlining does not
depend on adding `#[Pure]`.
Whim (which is partly my experiment for trying these language ideas)
also inlines eligible calls by default. It has two attributes to
influence that decision:
- [`#[Whim\Marker\AlwaysInline]`](whim/lib/src/Marker/AlwaysInline.whim at c159232651a7b9bc6588af2dffafdcdbc556164b · carthage-software/whim · GitHub)
relaxes some limits; it requests inlining but does not guarantee it.
- [`#[Whim\Marker\NeverInline]`](whim/lib/src/Marker/NeverInline.whim at c159232651a7b9bc6588af2dffafdcdbc556164b · carthage-software/whim · GitHub)
prevents inlining.
The [inliner checks the function’s
bytecode](whim/crates/optimizer/src/passes/inline_leaf_calls/leaf.rs at c159232651a7b9bc6588af2dffafdcdbc556164b · carthage-software/whim · GitHub),
and even [allows output
instructions](whim/crates/optimizer/src/passes/inline_leaf_calls/leaf.rs at c159232651a7b9bc6588af2dffafdcdbc556164b · carthage-software/whim · GitHub).
Inlining a function that prints something still needs to print it at
the same point in execution.
You can see [this example in the
playground](Shared Whim 0.14.0 Result | Whim Playground),
the “optimized bytecode” tab shows no function calls because Whim
inlines both the pure `sum()` and the impure `greet()`, then folds the
arithmetic and string concatenations, without any attributes.
Purity can help other optimizations, such as reusing results or
evaluating calls with constant arguments, depending on the exact
contract. But that is a separate case from inlining.
Enforcing purity can also have a cost. As Pedro notes, known call
targets may allow compile-time checks, while dynamic calls whose
targets we cannot prove need runtime checks. Those checks might be
cheap, and some could be removed, but we should measure that rather
than assume `#[Pure]` makes calls cheaper.
Personally, I think having the language enforce that promise,
including for dynamic calls, could be a useful benefit even when
static analysis tools support similar annotations. Whether the
restrictions and runtime cost are worth it is a different question. I
just would not make inlining a condition for accepting it, since we
can improve that independently for both pure and impure code.
Cheers,
Seifeddine.