Hi

On 10/30/22 17:22, Larry Garfield wrote:
2. There are ample use cases for most operations to return an array or a lazy 
iterable.  Both totally exist.  I solved that by also having a separate version 
of each function, eg, amap() vs itmap().  The former returned an array, the 
latter returned a generator that generated the equivalent array.  It would be a 
fatal design flaw to not account for this.  Yes, this balloons the number of 
such functions, which sucks, but that's PHP for you.

Please not. Only provide functions that return an iterable without giving any further guarantees, because …

A possible alternative would be to always return a lazy iterable in all 
circumstances and assume someone can use to_array() or equivalent on the result 
if they want it as an array.  (That's effectively what Python 3 does with 
comprehensions.)  However, that could have non-trivial performance impact since 
generators are slower than plain arrays.

… this works. The users should not need to think about what return type is more useful for their specific use case.

The heavy lifting / optimization should be left to the compiler / engine, similarly how a fully qualified 'is_null()' is also optimized, such that no actual function call happens [1].

Even if this kind of optimization does not happen in the first version, this should not be a reason to clutter the API surface.

3. Feel free to borrow liberally, design-wise, from the above code.  There's a 
few more methods in there that could be of use, too.  Note, though, that all 
are designed to be used with a pipe(), so they mostly return a closure that has 
been manually partially applied with everything except the iterable, so you get 
a single-argument function, which is what a pipe() or compose() chain needs.

Third, speaking of pipe, I disagree with Tim that putting the callback first 
would be easier for pipe/partials.  If we ever get partials similar to 
previously implemented, then the argument order won't matter.  If we get pipes 
as I've previously proposed, then none of these functions are directly usable 
because they're multi-argument.

The alternative I've considered is somewhat inspired by Elixir (assuming I understand the 
little Elixir I've read), in which a function after a |> is automatically assumed to be 
partially applying everything but the first argument.  So $list |> map($callable) translates 
to map($list, $callable).  I've not decided yet if that's a good way to avoid needing full 
partial application or a good way to make horribly confusing code.  But if that were to happen, 
it would only work if all of these functions took the iterable, the "object to be operated 
on", as their first argument.


With Haskell, from which my functional programming experience comes, it's the exact inverse: All functions are automatically partially applied [2], thus in a pipe the "missing" parameter comes last, not first as with Elixir.

So I guess there is no right or wrong and it depends on the programming language which variant feels more natural. I still prefer having the callback first for the reasons I've outlined, but in the end I'm happy as long as it's consistent.

Best regards
Tim Düsterhus

[1] https://github.com/php/php-src/blob/e00dadf43a17da3bb79aba360d07e29b359c12b3/Zend/zend_compile.c#L4372-L4373 [2] You can just do 'lengthOfAll = map length' and then 'lengthOfAll ["foo", "bar", "foobar"]'

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to