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));
--
Rowan Tommins
[IMSoP]