Hi
On 2/7/25 22:04, Larry Garfield wrote:
I split pipes off from the Composition RFC late last night right before
posting; I guess I missed a few things while doing so. :-/ Most notably, the
Compose section is now removed from pipes, as it is not in scope for this RFC.
(As noted, it's going to be more work so has its own RFC.) Sorry for the
confusion. I think it should all be handled now.
The “Introduction” section still talks about function composition rather
than the pipe operator, I believe.
5. The “References” (as in reference variables) section would do well
with an example of what doesn't work.
Example block added.
I don't understand that example. If I would write this as regular
function calls it works fine. Did you mean to compare against:
inc_print(['a' => 'A', 'b' => 'B']);
i.e.
['a' => 'A', 'b' => 'B'] |> inc_print(...);
? If not, then you will need to expand on “breaks” which is a
non-technical term.
9. In the “Why in the engine?” section: The RFC makes a claim about
performance.
Do you have any numbers?
Not currently. The statements here are based on simply counting the number of
function calls necessary, and PHP function calls are sadly non-cheap. In
previous benchmarks of my own libraries using my Crell/fp library, I did find
that the number of function calls involved in some tight pipe operations was
both a performance and debugging concern, but I don't have any hard numbers
laying about at present to share.
If you think that's critical, please advise on how to best get meaningful
numbers here.
Not sure if I missed the dedicated performance section on my first read
through the RFC or if it is actually new. It also claims:
> The result is that pipe has virtually no runtime overhead.
Which given your claim that “function calls are non-cheap” and combined
with the intermediate closure for calls taking more than one parameter
is contradictory.
Generally speaking, if your RFC makes a claim (about performance), then
it needs to back this up by evidence and not with feelings.
Regarding the “How”:
A hyperfine
(https://tideways.com/profiler/blog/how-we-use-hyperfine-to-measure-php-engine-performance)
comparison for a release build comparing:
1. An implementation based on regular function calls without
intermediate variables.
2. An implementation based on regular function calls with an
intermediate temporary variable.
3. A performance-optimized userland pipe operator implementation.
4. The pipe operator RFC.
would certainly appropriate to gain a first insight.
Having an OPcode dump to compare (1) against (4) would help gain more
insights as to where the performance differences come from.
If the expression on the right side that produces a Closure has side effects
(output, DB interaction, etc.), then the order in which those side effects
happen may change with the different restructuring.
That is a good point. I see you added a precedence section, but this
does not fully explain the order of operations in face of side-effects
and more generally with regard to “short-circuiting” behavior. An OPcode
dump would explain that.
Specifically for:
function foo() { echo __FUNCTION__, PHP_EOL; return 1; }
function bar() { echo __FUNCTION__, PHP_EOL; return false; }
function baz($in) { echo __FUNCTION__, PHP_EOL; return $in; }
function quux($in) { echo __FUNCTION__, PHP_EOL; return $in; }
foo()
|> (bar() ? baz(...) : quux(...))
|> var_dump(...);
What will the output be?
but using the temp-var approach makes dealing with references easier
I thought the RFC said that references were disallowed?
Best regards
Tim Düsterhus