Hi Rowan On Thu, Apr 3, 2025 at 1:59 PM Rowan Tommins [IMSoP] <imsop....@rwec.co.uk> wrote: > > At first, I thought Ilija's example looked pretty neat, but having > thought about it a bit more, I think the "first-arg" approach makes a > handful of cases nicer at the cost of a lot of magic, and making other > cases worse.
I think "handful" is the word to focus on. As noted, I believe the primary use-case for pipes are iterators. If that's true, then an implicit first-arg approach should cover the majority of examples, while complicating the rest. Whether that's a worthwhile trade-off is for the community to decide. To me, pipes improve readability when they behave like methods, i.e. they perform some operation on a subject. This resembles Swift's protocol extensions or Rust's trait default implementations, except using a different "method" call operator. With this mental model, the first-arg approach seems intuitive to me. Once parameters are out of order, the pipe examples with partial function application cause more cognitive overhead for me, but this is entirely subjective. > If we have a special case where the right-hand side *is* an expression, > evaluated as a single-argument callable/Closure, that's even more scope > for confusion. [cf my thoughts in the async thread about keeping the > right-hand side of "spawn" consistent] To clarify: I'm not in favor of this syntax either. While I originally mentioned it as a possibility, I later noted that `lhs |> {rhs}` would be less ambiguous, given that {} is not legal in the general expression context, while also resembling the `lhs->{rhs}` syntax to a degree. However, because {} is not simpler than `lhs |> rhs()`, I mentioned neither in my e-mail. > The cases it makes nicer are where you are chaining existing functions > with the placeholder as first (but not only) parameter. If we decide not to add an iterator API that works well with first-arg, then I agree that this is not the right approach. But if we do, then neither of your examples are problematic. > // first-arg chaining > $someChain |> fn($string) => explode(':', $string)(); As for string functions, I had a quick look through the stubs and could only find a handful of functions that are not already subject-first: * preg_*/mb_ereg* * mb_split * explode Maybe my search was flawed, let me know if there are any that I missed. explode() specifically usually appears first in a chain (or deepest in nested calls), which means it could just remain a normal function call. $result = explode(' ', $str) |> filter(...) |> map(...) |> join(' '); The iterator API would improve the array_filter() example. Admittedly, you might not always want to use iterators. A single array_map() would likely be faster than going through the iterator API. But then again, single calls aren't chains, so they won't benefit much from pipes to begin with. Ilija