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