A little syntactic sugar on array_* function calls?

php.internals

Karoly Negyesi

5 years ago
Hi, I was wondering whether $array->map($somefunction) would be possible. I am not a C programmer by any stretch but reading ZEND_VM_HOT_OBJ_HANDLER(112 it seems to me it should be quite easy (famous last words) to find out if object is an array and if so then 1. prepend the string array_ before the method name 2. based on a small lookup table move the "object" to the right place -- either first argument or second. 3. do a function call instead of a method call. Regarding #2 by default it's the first: $array->flip() becomes array_flip($array) $array->column($column_key, $index_key) becomes array_column($array, $column_key, $index_key) $array->merge($array2, $array3) becomes array_merge($array, $array2, $array3) There'd be a small list of methods/functions where it's the second, for example: $array->map($fn) becomes array_map($fn, $array) $array->search($needle, $strict) becomes array_search($needle, $array, $strict) $array->key_exists($key) becomes array_key_exists($key, $array) (Is there even any other?) For phase 1 we could skip the functions which gets the first argument by reference (walk, sort) and figure it out later. Hand waving yes but never let perfect stand in the way of good enough :) Look how nicely this reads: $array->map($fn)->filter($fn2) Compared to array_filter(array_map($fn, $array), $fn2) I see no BC concerns here because in any previous PHP versions this is a fatal error. I do not see any syntax ambiguity either but here I am probably just naive. It could also be usable with user defined functions. What do you think? Karoly Negyesi

Hans Henrik Bergan

5 years ago
fwiw this can be implemented in userland, and i bet someone already made a composer package for it ^^ On Tue, 25 May 2021 at 11:20, Karoly Negyesi <karoly@negyesi.net> wrote:

Илья Сомов

5 years ago
> I was wondering whether $array->map($somefunction) would be possible.
There is Pipe Operator RFC existing already, which most probably would suit your needs. The code you want will look like this: https://wiki.php.net/rfc/pipe-operator-v2 ```php $array |> array_map($somefunction). ``` Best wishes, someniatko

Илья Сомов

5 years ago
Sorry, I made a typo here. In combination with the proposed Partial Function Application RFC (https://wiki.php.net/rfc/partial_function_application), which is however is under active discussion, and changes will probably be made to it, it would look roughly like this: ```php $array |> array_map($somefunction, ?); ``` Best wishes, someniatko

Lynn

5 years ago
On Tue, May 25, 2021 at 11:31 AM Hans Henrik Bergan <divinity76@gmail.com> wrote:
> fwiw this can be implemented in userland, and i bet someone already made a > composer package for it ^^ >
Not everyone is interested in doing `$array = new ArrayWrapper($originalArray)` and then breaking all `array` parameters. There are a bunch of different packages and people prefer different packages on top of that. Some packages will have feature X and others will have feature Y, and that makes it even harder to properly use. The downside of having this in PHP will obviously be that it's much less flexible than a userland implementation. I'd be very happy to see it in PHP while I won't even bother looking for array wrappers in userland. On Tue, May 25, 2021 at 12:05 PM someniatko <someniatko@gmail.com> wrote:
> There is Pipe Operator RFC existing already, which most probably would > suit your needs. The code you want will look like this: > https://wiki.php.net/rfc/pipe-operator-v2 > > ```php > $array |> array_map($somefunction). > ```
The pipe operator feels like a poor solution while `->` would do exactly what people want. On Tue, May 25, 2021 at 12:05 PM someniatko <someniatko@gmail.com> wrote:

Илья Сомов

5 years ago
> The pipe operator feels like a poor solution while `->` would do exactly what people want.
Could you elaborate? Adding method-like array access functions with only few predefined functions, and only for arrays looks very limited in scope, while the pipe operator would allow applying any existing function, be it internal or userland one, to any type of variable, not limited by arrays, having the same "fluent api" feel. Also it clearly distincts between object method calls and function application, which is a plus for clarity, IMO. I am also not sure who the "people" you refer to are, because, well, I am among the people using PHP daily, and I would personally prefer a more generic solution, which the Pipe Operator currently is. Best wishes, someniatko

Karoly Negyesi

5 years ago
Thanks for your quick feedback everyone. On Tue, May 25, 2021 at 3:22 AM someniatko <someniatko@gmail.com> wrote:
> > The pipe operator feels like a poor solution while `->` would do exactly > what people want. > > Could you elaborate? Adding method-like array access functions with > only few predefined functions, and only for arrays looks very limited >
That is probably because it is very limited :) deliberately so. The proposed syntax $array |> array_map($fn1, ?) |> array_filter(?, $fn2) When I compare to: $array->map($fn1)->filter($fn2) 1. It's longer. Much longer. 2. It still requires knowing where the array goes. That's legacy which we could sidestep with the arrow notation. 3. Admittedly, the pipe is much more powerful. But why not both :) ? Also, it would require accepting two RFCs although of course mine would need to become an RFC too and longer term this is not a problem. scalar_objects are wonderful. It's a space rocket compared to my wheelbarrow. If it flies , mine is obviously moot. However, a wheelbarrow is much cheaper and quicker to construct than a spaceship :D Once again, what I propose here wants to be a simple, cheap to implement, narrow quickfix. (Although users can add array_foobar($array, $arg1...) for $array->foobar($arg1) as they need.) Karoly Negyesi

Илья Сомов

5 years ago
> The proposed syntax > > $array |> array_map($fn1, ?) |> array_filter(?, $fn2) > > When I compare to: > > $array->map($fn1)->filter($fn2) > > 1. It's longer. Much longer. > 2. It still requires knowing where the array goes. That's legacy which we could sidestep with the arrow notation. > 3. Admittedly, the pipe is much more powerful.
While the argument No. 2. is completely valid, the 1st one is not so. If you remove whitespaces around the `|>` and also if you alias these functions to `map` and `filter` respectively (or if, for instance, some future RFC moves them into a special `PHP\Array` namespace, which would probably never happen, but it's allowed to dream), it could look like this: ```php $array|>map($fn1, ?)|>filter(?, $fn2); $array->map($fn1)->filter($fn2); ``` A bit longer (due to 2.), but not that much, actually. Best wishes, someniatko

Hendra Gunawan

5 years ago
Hello.
> > ```php > $array|>map($fn1, ?)|>filter(?, $fn2); > $array->map($fn1)->filter($fn2); > ``` >
Whitespace removal is not a solution for code length problems. You might have a new problem if you do it. "|" is very similar to the lowercase "L" and uppercase "i". It's just an extra 3 characters (", ?" or "?, "). For most people, this is not a problem at all. people tend to write "one statement per line" rather than "multi statement line". I myself usually write no more than 3 statements per line if they are less than 120 characters. The real problem is there is no consistency for "haystack vs needle" position. There are RFCs to fix this (along with the naming convention problem), but none of them are successful.
> The pipe operator feels like a poor solution while "->" would do > exactly what people want.
Not so poor if we * use "~>" as pipe operator rather than "|>" * redesign the api under their proper namespace and strictly place the "haystack" as the first function argument. Regards, Hendra Gunawan.

Iván Arias

5 years ago
Hi all, It sounds like scalar objects by Nikita: https://github.com/<https://github.com/nikic/scalar_objects>nikic<https://github.com/nikic/scalar_objects>/scalar_objects<https://github.com/nikic/scalar_objects> Regards, Iván Arias. Get Outlook for Android<https://aka.ms/AAb9ysg> ________________________________ From: Hendra Gunawan <the.liquid.metal@gmail.com> Sent: Tuesday, May 25, 2021 10:58:46 PM To: someniatko <someniatko@gmail.com> Cc: Karoly Negyesi <karoly@negyesi.net>; Marco Pivetta <ocramius@gmail.com>; Lynn <kjarli@gmail.com>; internals@lists.php.net <internals@lists.php.net> Subject: Re: [PHP-DEV] A little syntactic sugar on array_* function calls? Hello.
> > ```php > $array|>map($fn1, ?)|>filter(?, $fn2); > $array->map($fn1)->filter($fn2); > ``` >
Whitespace removal is not a solution for code length problems. You might have a new problem if you do it. "|" is very similar to the lowercase "L" and uppercase "i". It's just an extra 3 characters (", ?" or "?, "). For most people, this is not a problem at all. people tend to write "one statement per line" rather than "multi statement line". I myself usually write no more than 3 statements per line if they are less than 120 characters. The real problem is there is no consistency for "haystack vs needle" position. There are RFCs to fix this (along with the naming convention problem), but none of them are successful.
> The pipe operator feels like a poor solution while "->" would do > exactly what people want.
Not so poor if we * use "~>" as pipe operator rather than "|>" * redesign the api under their proper namespace and strictly place the "haystack" as the first function argument. Regards, Hendra Gunawan.
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php

Unnamed Person

5 years ago
Hello, I read about this extension times ago but didn't know whether it had been public. If Nikita is reading this, I request him to think of proposing a modified version of this extension bundled with PHP. In simple words, he can hide the function that registers a class that serves as a prototype of a built-in type. And, also provide with scalar methods for string, int, float and arrays. To get handler registering functionality added to core, there should be a separate RFC. While I read this thread for the first time, I had following suggestions: 1. All array functions should be moved to its scaler object, and the word "array_" should also be removed. 2. To maintain backward compatibility, all array_* functions will become method aliases of scaler array. 3. ArrayObject will also exist for the compatibility purpose, and its methods will also be added to the scaler array. Thus, array() or [] will return scaler array object, and following syntax would become valid: `[1,2,3,4,5,6,7,8,9] -> reverse();` `array(1 => 'a', 2 => 'b') -> flip();` If it happens, users will automatically be stopped from passing non-array values to array functions, and the error will be caught earlier. Regards On 5/26/21, Iván Arias <txigreman@hotmail.com> wrote:

Hossein Baghayi

5 years ago
On Wed, 26 May 2021 at 10:14, Hamza Ahmad <office.hamzaahmad@gmail.com> wrote:
> Thus, array() or [] will return scaler array object, >
Hello, This doesn't seem trivial to me. I mean, should array object be passed by value or by reference? Arrays are passed by value by default so far, and objects are be-ref internally. If we are to have array object, will it be exceptional? Or should we change its behaviour going forward? To be clear, array() returns an array right now, which by default is passed by value at the moment. If it was supposed to be changed to an object, (array() to return an object), should it be still passed by value? (an exceptional object) or we should change its behaviour going forward and pass it by-ref? Either way, it may have some quirks associated with it.

Mike Schinkel

5 years ago
> On May 25, 2021, at 6:28 PM, Iván Arias <txigreman@hotmail.com> wrote: > > Hi all, > > It sounds like scalar objects by Nikita: https://github.com/nikic/scalar_objects
Yes, but Nikita wrote this note about technical limitations at the bottom of the repo README: Due to technical limitations, it is not possible to create mutable APIs for primitive types. Modifying $self within the methods is not possible (or rather, will have no effect, as you'd just be changing a copy). Does that mean that the scope of Nikita's proof-of-concept could not modify $self, or that it is simply not possible to modify $self given limitations inherent in PHP? Further, does that only apply to scalars, or might possible arrays could be different? -Mike

Marco Pivetta

5 years ago
On Wed, May 26, 2021 at 1:03 PM Mike Schinkel <mike@newclarity.net> wrote:
> > > > On May 25, 2021, at 6:28 PM, Iván Arias <txigreman@hotmail.com> wrote: > > > > Hi all, > > > > It sounds like scalar objects by Nikita: > https://github.com/nikic/scalar_objects > > Yes, but Nikita wrote this note about technical limitations at the bottom > of the repo README: > > Due to technical limitations, it is not possible to create mutable APIs > for primitive types. Modifying $self within the methods is not possible (or > rather, will have no effect, as you'd just be changing a copy). >
Sounds like a big **advantage**? Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Unnamed Person

5 years ago
> should array object be passed by value or by reference? > If we are to have array object, will it be exceptional?
By value. Because array is a data type, we are talking about making it behave like object. In JavaScript, Arrays, Strings, and Numbers are objects; they have their respective properties and methods. Still, when they are passed to a function or a method call, they are passed by value, not by reference. We should pass arrays by value because it will let a function or a method modify it without changing the original array. If we make it a regular object, it will be a bc break. So, whenever a callable modifies an array, it will modify a variable out of its scope. I am talking about attaching some methods and properties to array (or largely, the string, int and float) type. In your manner, an exceptional array object that is passed by value, Which will have "key_first", "key_last", "keys", "values", "length", "type" (if PHP later introduces typed arrays), and "is_list" as properties, and "reverse", "flip", "map", "filter", "walk" and so on as methods. Such methods will be performed on a value, not a variable. In other words: `[1,2,3,4,5,6,7,8,9,0]->print();` will work. This way, it does not matter whether one modifies a variable or a value. According to the implementation of Nikita's extension, there will be functions attached to each method of a type. To remove this limitation, what if array is an internal array object? To make my previous statement regarding making array_* functions as method aliases for array->* methods, I give the example of mysqli. It has both ways of interaction, object-oriented and procedural. So, why not this with arrays? Regards On 5/26/21, Mike Schinkel <mike@newclarity.net> wrote:

Hendra Gunawan

5 years ago
Hello.
> > Yes, but Nikita wrote this note about technical limitations at the bottom of the repo README: > > Due to technical limitations, it is not possible to create mutable APIs for > primitive types. Modifying $self within the methods is not possible (or > rather, will have no effect, as you'd just be changing a copy). >
If it is solved, this is a great accomplishment for PHP. But I think scalar object is not going anywhere in the near future. If you are not convinced, please take a look https://github.com/nikic/scalar_objects/issues/20#issuecomment-569520181. This makes me have a strong feeling about pipe operator greater than before to solve object-style for scalar issue. I hope that someone will take an initiative to fix the old inconsistent and confusing API. Pipe operator+new API is a better solution than no solution at all.

A.L.E.C

5 years ago
On 25.05.2021 12:40, Karoly Negyesi wrote:
> $array->map($fn1)->filter($fn2) > > 1. It's longer. Much longer. > 2. It still requires knowing where the array goes. That's legacy which we > could sidestep with the arrow notation. > 3. Admittedly, the pipe is much more powerful.
I agree. A unified object oriented interface to arrays (or strings for that matter) is not a new topic on this list. I guess you'd have to start with collecting all methods that would need to be implemented. First stage could be all array_* functions, but I can imagine others e.g. sorting functions to be included.
> Once again, what I propose here wants to be a simple, cheap to implement, > narrow quickfix.
I'm afraid it's much more complicated than you think.
> (Although users can add array_foobar($array, $arg1...) for > $array->foobar($arg1) as they need.)
I wouldn't go that far with this (possible BC break), maybe a future scope.
-- Aleksander Machniak Kolab Groupware Developer [https://kolab.org] Roundcube Webmail Developer [https://roundcube.net] ---------------------------------------------------- PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

Marco Pivetta

5 years ago
Heyo, On Tue, May 25, 2021 at 11:20 AM Karoly Negyesi <karoly@negyesi.net> wrote:
> Hi, > > I was wondering whether $array->map($somefunction) would be possible. I am > not a C programmer by any stretch but reading ZEND_VM_HOT_OBJ_HANDLER(112 > it seems to me it should be quite easy (famous last words) to find out if > object is an array and if so then > > 1. prepend the string array_ before the method name > 2. based on a small lookup table move the "object" to the right place -- > either first argument or second. > 3. do a function call instead of a method call. > > Regarding #2 by default it's the first: > > $array->flip() becomes array_flip($array) > $array->column($column_key, $index_key) becomes array_column($array, > $column_key, $index_key) > $array->merge($array2, $array3) becomes array_merge($array, $array2, > $array3) > > There'd be a small list of methods/functions where it's the second, for > example: > > $array->map($fn) becomes array_map($fn, $array) > $array->search($needle, $strict) becomes array_search($needle, $array, > $strict) > $array->key_exists($key) becomes array_key_exists($key, $array) > > (Is there even any other?) > > For phase 1 we could skip the functions which gets the first argument by > reference (walk, sort) and figure it out later. Hand waving yes but never > let perfect stand in the way of good enough :) > > Look how nicely this reads: > > $array->map($fn)->filter($fn2) > > Compared to array_filter(array_map($fn, $array), $fn2) > > I see no BC concerns here because in any previous PHP versions this is a > fatal error. I do not see any syntax ambiguity either but here I am > probably just naive. It could also be usable with user defined functions. > > What do you think? >
Have you seen https://github.com/nikic/scalar_objects ? Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Sara Golemon

5 years ago
On Tue, May 25, 2021 at 4:20 AM Karoly Negyesi <karoly@negyesi.net> wrote:
> I was wondering whether $array->map($somefunction) would be possible. I am > not a C programmer by any stretch but reading ZEND_VM_HOT_OBJ_HANDLER(112 > it seems to me it should be quite easy (famous last words) to find out if > object is an array and if so then > > 1. prepend the string array_ before the method name > 2. based on a small lookup table move the "object" to the right place -- > either first argument or second. > 3. do a function call instead of a method call. >
While I don't love the specifics of the proposal, I am 100% in favor of allowing arrays to be used in an object-like fashion. What I don't like about the specific proposal is that it's just a little too magic in its function selection and argument mapping. There's also the fact that it doesn't leave room to improve specifics about the implementations of the methods. I'd much rather seen an `Array` class defined with specific methods declared on it. In many cases these will be simple trampolines to an existing function, but it gives us self-documenting stubs and room to wiggle out of poor decisions from the 1990s. Such a class would not be instantiable or inheritable, it would just exist as a lightweight ValueObject for performing the method invocations (we can make it internally instantiable using tricks like not calling a private constructor). Then some hand-wavey details about maybe returning objects which have a cast-to-array handler, mumble mumble, devil in the details... waving hands... -Sara

Mike Schinkel

5 years ago
> On May 26, 2021, at 2:34 PM, Sara Golemon <pollita@php.net> wrote: > > What I don't like about the specific proposal is that it's just a little > too magic in its function selection and argument mapping. There's also the > fact that it doesn't leave room to improve specifics about the > implementations of the methods. I'd much rather seen an `Array` class > defined with specific methods declared on it.
Wouldn't an `Array` class necessarily result in array-incompatible pass-by-reference semantics, which is one of the same issues with userland using ArrayObject as an array replacement?
> On May 26, 2021, at 7:51 AM, Marco Pivetta <ocramius@gmail.com> wrote: > > On Wed, May 26, 2021 at 1:03 PM Mike Schinkel <mike@newclarity.net <mailto:mike@newclarity.net>> wrote: > > > > On May 25, 2021, at 6:28 PM, Iván Arias <txigreman@hotmail.com <mailto:txigreman@hotmail.com>> wrote: > > > > Hi all, > > > > It sounds like scalar objects by Nikita: https://github.com/nikic/scalar_objects <https://github.com/nikic/scalar_objects> > > Yes, but Nikita wrote this note about technical limitations at the bottom of the repo README: > > Due to technical limitations, it is not possible to create mutable APIs for primitive types. Modifying $self within the methods is not possible (or rather, will have no effect, as you'd just be changing a copy). > > Sounds like a big **advantage**?
Yes, it is a big advantage. Except for when it is not. -Mike

Sara Golemon

5 years ago
On Wed, May 26, 2021 at 2:36 PM Mike Schinkel <mike@newclarity.net> wrote:
> On May 26, 2021, at 2:34 PM, Sara Golemon <pollita@php.net> wrote: > > > What I don't like about the specific proposal is that it's just a little > too magic in its function selection and argument mapping. There's also the > fact that it doesn't leave room to improve specifics about the > implementations of the methods. I'd much rather seen an `Array` class > defined with specific methods declared on it. > > > Wouldn't an `Array` class necessarily result in array-incompatible > pass-by-reference semantics, which is one of the same issues with userland > using ArrayObject as an array replacement? > >
It would if the Array objects got returned. I'm instead picturing an instance that magically comes into being solely for the duration of the method call. Once the method returns, the object vanishes. -Sara

Larry Garfield

5 years ago
On Wed, May 26, 2021, at 4:31 PM, Sara Golemon wrote:
> On Wed, May 26, 2021 at 2:36 PM Mike Schinkel <mike@newclarity.net> wrote: > > > On May 26, 2021, at 2:34 PM, Sara Golemon <pollita@php.net> wrote: > > > > > > What I don't like about the specific proposal is that it's just a little > > too magic in its function selection and argument mapping. There's also the > > fact that it doesn't leave room to improve specifics about the > > implementations of the methods. I'd much rather seen an `Array` class > > defined with specific methods declared on it. > > > > > > Wouldn't an `Array` class necessarily result in array-incompatible > > pass-by-reference semantics, which is one of the same issues with userland > > using ArrayObject as an array replacement? > > > > > It would if the Array objects got returned. I'm instead picturing an > instance that magically comes into being solely for the duration of the > method call. Once the method returns, the object vanishes. > > -Sara
It sounds like you're describing something more akin to "extensions" in C#, or the way trait impls work in Rust, or the way methods get defined in Go. (All of which would be quite neat, but I don't know how they'd play nicely in PHP.) --Larry Garfield