Re: Pipe precedence challenges
| From: | Rob Landers | Date: | Fri, 22 Aug 2025 09:40:46 +0000 |
| Subject: | Re: Pipe precedence challenges | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-128524@lists.php.net to get a copy of this message | ||
On Thu, Aug 21, 2025, at 15:53, Larry Garfield wrote:
> On Thu, Aug 21, 2025, at 4:07 AM, Hans Krentel wrote:
> > On Thursday 14 August 2025 21:30:08 (+02:00), Larry Garfield wrote:
> >
> >> Does anyone have a better solution to suggest?
> >
> > I've been following the discussion on the pipe/short-closure precedence
> > issue, and while the parentheses-based solution works, it does lead to
> > syntactic noise that undermines the readability benefits of pipes.
> >
> > I'd like to suggest an alternative that turns this problem into an
> > opportunity for a more elegant syntax.
> >
> > Inspired by prior art like Haskell's
do notation, I
> > propose what I
> > call "short closure canned pipes." The idea is to leverage the greedy
> > nature of short closures to define a clean, linear pipe chain within a
> > closure context. Instead of fighting the precedence, we embrace it to
> > create a new form:
> >
> >
> > Current problematic example (even with parentheses):
> > ----------------------------------------------------
> >
> > fn($x) => ($x
> > |> (fn($x) => array_map(strtoupper(...), $x))
> > |> (fn($x) => array_filter($x, fn($v) => $v != 'O'))
> > );
> >
> > Proposed "canned pipes" syntax:
> > -------------------------------
> >
> > fn($x) =>
> > |> array_map(strtoupper(...), $x)
> > |> array_filter($x, fn($v) => $v != 'O')
> > ;
> >
> >
> > This syntax:
> >
> > * Resolves the ambiguity: The fn($x) => explicitly
> > "cans" the value
> > for the entire pipe chain, making the precedence clear without
> > parentheses.
> >
> > * Reduces boilerplate: It eliminates the repetition of fn($x)
> > => and
> > nested parentheses, making the code more readable and maintainable.
> >
> > * Aligns with intent: It reads as a simple sequence of transformations,
> > which is the core goal of the pipe operator.
> >
> > This approach effectively provides syntactic sugar for the common case
> > of processing a value through a pipe chain within a closure. It turns
> > the late-discovered defect into a win for developer ergonomics and
> > future-proofing.
> >
> > I understand that implementation feasibility needs to be assessed, but
> > if possible, this could be a more satisfying solution than mandatory
> > parentheses. Thanks to the team for highlighting this issue -- it's
> > sparked a creative way to enhance the feature.
> >
> > Would love to hear thoughts on this.
> >
> > Best,
> > hakre
>
> That's an interesting idea, but I don't think it solves the problem.
>
> It would make the syntax for pipes-in-closures nicer, yes. However:
>
> 1. Ideally, I'm hoping such a pipe-in-closure is replaced with a Composition operator soon
> enough (https://wiki.php.net/rfc/function-composition), so that wouldn't even be necessary.
> 2. The main problem is not when a pipe is inside a closure, but when a closure is inside a
> pipe, which is likely the far more common case.
>
> $input
> |> fn($x) => array_map(strtoupper(...), $x)
> |> fn($x) => array_filter($x, fn($v) => $v != 'O')
> ;
>
> The problem is this gets interpreted "backwards", with the first closure wrapping
> around second, rather than being a sibling of it in a pipe chain. A short-hand for
> pipes-in-closures wouldn't address this issue, which is the more pressing concern.
>
> The annoying extra parens there would also be resolved using PFA, which side steps the arrow
> function in the common case. As the issue is the syntactic parsing of arrow functions, not having
> arrow functions would also resolve the issue. (As would using higher-order functions instead, which
> frankly is what I am likely to do 90% of the time myself.)
>
> --Larry Garfield
>
I think the point Hans was making is that the fn($x) becomes implicit so you don't even need
it, so your example would be something like this in this case:
$input |> fn($x) => |> array_map(strtoupper(...), $x) |> array_filter($x, fn($v) =>
$v != 'O');
— Rob