On Fri, Feb 7, 2025, at 22:04, Larry Garfield wrote:
> Merging a few replies together here, since they overlap.  Also reordering a 
> few of Tim's comments...
> 
> On Fri, Feb 7, 2025, at 7:32 AM, Tim Düsterhus wrote:
> > Hi
> >
> > Am 2025-02-07 05:57, schrieb Larry Garfield:
> >> It is now back with a better implementation (many thanks to Ilija for 
> >> his help and guidance in that), and it's nowhere close to freeze, so 
> >> here we go again:
> >> 
> >> https://wiki.php.net/rfc/pipe-operator-v3
> >
> > There's some editorial issues:
> >
> > 1. Status: Draft needs to be updated.
> > 2. The RFC needs to be added to the overview page.
> > 3. List formatting issues in “Future Scope” and “Patches and Tests”.
> >
> > Would also help having a closed voting widget in the “Proposed Voting 
> > Choices” section to be crystal clear on what is being voted on (see 
> > below the next quote).
> 
> 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.
> 
> > 5. The “References” (as in reference variables) section would do well 
> > with an example of what doesn't work.
> 
> Example block added.
> 
> > 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.
> 
> Regarding the equivalency of pipes:
> 
> Tim Düsterhus wrote:
> > 4. “That is, the following two code fragments are also exactly 
> > equivalent:”.
> >
> > I do not believe this is true (specifically referring to the “exactly” 
> > word in there), since the second code fragment does not have the short 
> > closures, which likely results in an observable behavioral difference 
> > when throwing Exceptions (in the stack trace) and also for debuggers. Or 
> > is the implementation able to elide the the extra closure? (Of course 
> > there's also the difference between the temporary variable existing, 
> > with would be observable for `get_defined_vars()` and possibly 
> > destructors / object lifetimes).
> 
> Thomas Hruska wrote:
> > The repeated assignment to $temp in your second example is _not_ 
> > actually equal to the earlier example as you claim.  The second example 
> > with all of the $temp variables should, IMO, just be:
> >
> > $temp = "Hello World";
> > $result = array_filter(array_map('strtoupper', 
> > str_split(htmlentities($temp))), fn($v) { return $v != 'O'; });
> 
> Juris Evertovskis wrote:
> > 3. Does the implementation actually turn `1 |> f(...) |> g(...)` into 
> > `$π = f(1); g($π)`? Is `g(f(1))` not performanter? Or is the engine 
> > clever enough with the var reuse anyways?
> 
> There's some subtlety here on these points.  The v2 RFC used the lexer to 
> mutate $a |> $b |> $c into the same AST as $c($b($a)), which would then 
> compile as though that had been written in the first place.  However, that 
> made addressing references much harder, and there's an important caveat 
> around order of operations. (See below.)  The v3 RFC instead uses a compile 
> function to take the AST of $a |> $b |> $c and produce opcodes that are 
> effectively equivalent to $t = $b($a); $t = $c($t);  I have not compared to 
> see if they are the precise same opcodes, but they net effect is the same.  
> So "effectively equivalent" may be a more accurate statement.
> 
> In particular, Tim is correct that, technically, the short lambdas would be 
> used as-is, so you'd end up with the equivalent of:
> 
> $temp = (fn($x) => array_map(strtoupper(...), $x))($temp);
> 
> I'm not sure if there's a good way to automatically unwrap the closure there. 
>  (If someone knows of one, please share; I'm fine with including it.)  
> However, the intent is that it would be largely unnecessary in the future 
> with a revised PFA implementation, which would obviate the need for the 
> explicit wrapping closure.  You would instead write
> 
> $a |> array_map(strtoupper(...), ?);
> 
> Alternatively, one can use higher order user-space functions already.  In 
> trivial cases:
> 
> function amap(Closure $fn): Closure {
>   return fn(array $x) => array_map($fn, $x);
> }
> 
> $a |> amap(strtoupper(...));
> 
> Which I am already using in Crell/fp and several libraries that leverage it, 
> and it's quite ergonomic.
> 
> There's a whole bunch of such simple higher order functions here:
> https://github.com/Crell/fp/blob/master/src/array.php
> https://github.com/Crell/fp/blob/master/src/string.php
> 
> Which leads to the subtle difference between that and the v2 implementation, 
> and why Thomas' statement is incorrect.  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.  With all pure functions, that won't make a practical 
> difference, and normally one should be using pure functions, but that's not 
> something PHP can enforce.
> 
> I don't think there would be an appreciable performance difference between 
> the two compiled versions, either way, but using the temp-var approach makes 
> dealing with references easier, so it's what we're doing.
> 
> Juris Evertovskis wrote:
> > 1. Do you think it would be hard to add some shorthand for `|> 
> > $condition ? $callable : fn($😐) => $😐`?
> 
> I'm not sure I follow here.  Assuming you're talking about "branch in the 
> next step", the standard way of doing that is with a higher order user-space 
> function.  Something like:
> 
> function cond(bool $cond, Closure $t, Closure $f): Closure {
>   return $cond ? $t : $f;
> }
> 
> $a |> cond($config > 10, bigval(...), smallval(...)) |> otherstuff(...);
> 
> I think it's premature to try and bake that logic into the language, 
> especially when I don't know of any other function-composition-having 
> language that does so at the language level rather than the standard library 
> level.  (There are a number of fun operations people build into pipelines, 
> but they are all generally done in user space.)
> 
> --Larry Garfield
> 

Put another way, what is the order of operations for this new operator?

For example, what is the output of

$x ? $y |> strlen(…) : $z

$x + $y |> sqrt(…) . EOL

Etc.

I noticed this seems to be missing from the RFC. As a new operator, I think it 
should be important to specify that. 

— Rob

Reply via email to