On Thu, Apr 3, 2025, at 6:58 AM, Rowan Tommins [IMSoP] wrote: > On 03/04/2025 08:22, Larry Garfield wrote: >> However, it also received significant pushback off-list from folks who felt >> it was too much magic. I don't want to torpedo pipes on over-reaching. But >> without feedback from other voters, I don't know if this is over-reaching. >> Is it? Please, someone tell me which approach you'd be more willing to vote >> for. :-) > > > 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. > > > The right-hand side is magic in two ways: > > 1) it looks like an expression, but actually has to be a syntactic > function call for the engine to inject an argument into > > 2) it looks like it's calling a function with the wrong arguments > > 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] > > > The cases it makes nicer are where you are chaining existing functions > with the placeholder as first (but not only) parameter. If you want to > pipe into a non-first parameter, you have a few options: > > a) Write a new function or explicit wrapper - equally possible with > either option > > // for first-arg chaining: > function swapped_explode(string $string, string $separator): string { > return explode($separator, $string); } > $someChain |> swapped_explode(':'); > > // for only-arg chaining: > function curried_explode(string $separator, string $string): callable { > return fn(string $string) => explode($separator, $string); } > $someChain |> curried_explode(':'); > > b) Use an immediate closure as the wrapper - only-arg chaining seems better > > // first-arg chaining > $someChain |> fn($string) => explode(':', $string)(); > > // first-arg chaining with special case syntax for closures > $someChain |> ( fn($string) => explode(':', $string) ); > > // for only-arg chaining: > $someChain |> fn($string) => explode(':', $string); > > c) Use a new partial application syntax - same problem as immediate closure > > // for first-arg chaining > $someChain |> explode(':', ?)(); > > // or with overloaded syntax > $someChain |> ( explode(':', ?) ); > > // for only-arg chaining > $someChain |> explode(':', ?); > > > It's also quite easy to write a helper for the special-case of > "partially apply all except the first argument": > > function partial_first(callable $fn, mixed ...$fixedArgs): callable { > return fn(mixed $firstArg) => $fn($firstArg, ...$fixedArgs); > } > > // first-arg chaining > $someChain |> array_filter(fn($v, $k) => $k === $v, ARRAY_FILTER_USE_BOTH); > > // native partial application > $someChain |> array_filter(?, fn($v, $k) => $k === $v, > ARRAY_FILTER_USE_BOTH); > > // workaround > $someChain |> partial_first(array_filter(...), fn($v, $k) => $k === $v, > ARRAY_FILTER_USE_BOTH));
Writing higher order functions to simulate first-arg is indeed quite straightforward. The RFC has some simple examples, and I've written a whole bunch of more robust ones here: https://github.com/Crell/fp/blob/master/src/array.php https://github.com/Crell/fp/blob/master/src/string.php The issue is performance. With foo(...), foo(?, 'bar'), or implicit first-arg, it's fairly straightforward to compile it down to a normal function call so there's no runtime cost. If you have an expression that produces a callable that gets used, that cannot be optimized away. So we could get this resulting syntax with either higher order user-space functions or with auto-first-arg: $foo |> map($fn1) |> filter($fn2) |> implode(','); However, if map() is a higher order function that returns a unary callable, there are two function invocations involved. If it's custom syntax that turns into map($foo, $fn1), then it's only one function invocation. So if we expect higher order functions to be common (and I would probably mainly use them myself), then it would be wise to figure out some way to make them more efficient. Auto-first-arg is one way. "Suck it up and use PFA with the ?" is another way that would work, but be less ergonomic. I'm not sure of other options off hand. --Larry Garfield