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]

Reply via email to