Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023 at 9:57 PM Deleu wrote: > > > > On Thu, Apr 20, 2023 at 8:23 PM Levi Morrison via internals > wrote: >> >> I'm going to stop here. Two big things: >> >> 1. I think reducing verbosity can be left for the future. We don't >> have to solve that right now. > > > >> What happens if I pass a short-closure? >> >> takeTwo(fn ($x, $y) => $x + $y); >> >> I would be annoyed if I had to write the type info, but particularly >> the return type. > > > Sorry for the unhelpful email, but does anybody else see the irony here? > It's just too funny to not be mentioned > > -- > Marco Deleu Sure, I get that ^_^ But the difference is that there are quite a few ways we can solve the first verbosity (allowing `fn` instead of `callable`, allowing type aliases which could also be useful for unions, etc), and only things that seem hard to solve the second one (static type inference? delayed type checks?) -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023 at 8:23 PM Levi Morrison via internals < internals@lists.php.net> wrote: > I'm going to stop here. Two big things: > > 1. I think reducing verbosity can be left for the future. We don't > have to solve that right now. > What happens if I pass a short-closure? > > takeTwo(fn ($x, $y) => $x + $y); > > I would be annoyed if I had to write the type info, but particularly > the return type. > Sorry for the unhelpful email, but does anybody else see the irony here? It's just too funny to not be mentioned -- Marco Deleu
[PHP-DEV] Expansion of PHP Symbols?
Hi Internals, sorry for the potential long email, but I can't sleep and I want to dump this out of my brain. Just a quick intro, let's recap PHP simple nature with this index.php file: ``` https://wiki.php.net/rfc/core-autoloading) and Callable Interfaces ( https://externals.io/message/120083) and particularly this comment https://externals.io/message/120083#120088 which I'll take a snippet here: Mainly the type alias question. Every time I see callable types discussed, > it immediately sidetracks into "how do we make that less fugly to write, > because callable types are naturally very verbose?" That leads directly to > typedefs/type aliases, which take one of two forms: type TwoInts = callable(int $x, int $y): int > type LinkedResponse = ResponseInterface > which raises autoloading questions and means a dependency on a type > defined in another package, in many cases Here I want to raise the discussion about the little I know about PHP symbols. If a symbol `interface Foo` is discovered and registered by PHP, it will be usable as an interface throughout its entire execution. If the symbol is not discovered and is used, it will trigger autoloading as a last-chance before fatal error. Can `type Number = int|number;` be a symbol? Can `type TwoInts = callable(int $x, int $y): int;` be a symbol? and lastly, can `function bar() {}` be a symbol? Here is how I see that unfolding: file-1.php ``` ``` file-2.php ``` ``` file-3.php ``` ``` How do we solve this? - Include/Require - Custom Autoloading - Composer/PSR-4 Sounds familiar? PHP already has a namespace to solve for grouping of types such that `\Foo\Bar` and `\Bar\Bar` can already co-exist because they are two different symbols. We already rely on classes/interfaces/enums defined on third-party packages, why not make type aliases work the same? And if functions could be symbols, it would work out the same as well. One limitation I see is that symbols cannot have conflicts. Enum/Interfaces/Classes cannot be named exactly the same under the exact same namespace. Type Alias would follow the same limitation. Function would be able to escape one part of that limitation if the engine could prefix every function name with `f_` or `fn_` when registering its symbol, making it backward compatible and allowing `class Foo` and `function Foo` co-exist, but not two functions called `Foo` (which is already an error anyway). Now one questionable user experience I see is defining: - 1 Class = 1 File - 1 Interface = 1 File - 1 Enum = 1 File (this is already annoying) - 1 function = 1 file - 1 symbol = 1 file But this user experience does not come from PHP's nature, but instead it comes from Composer/PSR-4 because PSR-4 maps namespaces to directories and symbols to files. We need a static analyser to scan our entire project, discover every symbol and create a mapping such as: - Symbol Class_Foo -> x.php - Symbol Interface_Bar -> x.php - Symbol Enum_Options -> y.php - Symbol Enum_ExtraOptions -> y.php - Symbol fn_foo -> z.php so that our registered autoload can include/require the necessary file when the symbol triggers an autoload. This static analyser already exists and is called `composer dump-autoload`. It just has not implemented a PSR-X which allows for this pattern? In conclusion PHP Scripts are very simple and dummy things and it already has a limitation of symbol discovery. We have already built tools to work with that limitation around autoloading/psr/composer. We could extend the PHP Symbol system to include functions and type alises. If this can be done in a backward-compatible manner, we can already integrate those into PSR-4 from day one. Lastly, we lift the social-construct limitation of 1 file = 1 symbol with PSR and Composer since PHP never really had this limitation built-in. We come out of it with 3 major wins (from my pov): - Function autoloading - Type Aliasing - Never creating 3 files for 3 Enums again If you managed to read up to here, I apologize for late confessing I know nearly nothing about PHP internals codebase. Is this obviously wrong and am I just hallucinating a new awesome PHP version here? -- Marco Deleu
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023 at 11:25 AM Larry Garfield wrote: > > Hi folks. This is a pre-discussion, in a sense, before a formal RFC. > Nicolas Grekas and I have been kicking around some ideas for how to address > the desire for typed callables, and have several overlapping concepts to > consider. Before going down the rabbit hole on any of them we want to gauge > the general feeling about the approaches to see what is worth pursuing. > > We have three "brain dump" RFCs on this topic, although these are all still > in super-duper early stages so don't sweat the details in them at this point. > We just want to discuss the basic concepts, which I have laid out below. > > https://wiki.php.net/rfc/allow_casting_closures_into_single-method_interface_implementations > https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement > https://wiki.php.net/rfc/structural-typing-for-closures > > ## The problem > > function takeTwo(callable $c): int > { > return $c(1, 2); > } > > Right now, we have no way to statically enforce that $c is a callable that > takes 2 ints and returns an int. We can document it, but that's it. > > There is one loophole, in that an interface may require an __invoke() method: > > interface TwoInts > { > public function __invoke(int $a, int $b): int; > } > > And then a class may implement TwoInts, and takeTwo() can type against > TwoInts. However, that works only for classes, which are naturally > considerably more verbose than a simple closure and represent only a subset > of the possible callable types. > > The usual discussion has involved a way to specify a callable type's > signature, like so: > > function takeTwo(callable(int $a, int $b): int $c) > { > return $c(1, 2); > } > > But that runs quickly into the problem of verbosity, reusability, and type > aliases, and the discussion usually dies there. > I'm going to stop here. Two big things: 1. I think reducing verbosity can be left for the future. We don't have to solve that right now. 2. I think there's another more important reason previous attempts failed: they will inevitably burden the programmer without type inference.For the moment, let's assume this signature: function takeTwo(callable(int $x, int $y): int $c); What happens if I pass a short-closure? takeTwo(fn ($x, $y) => $x + $y); I would be annoyed if I had to write the type info, but particularly the return type. Today, if I just used a static analysis tool, there's no problem: /** @param callable(int $x, int $y): int $c */ function takeTwo(callable $c); takeTwo(fn ($x, $y) => $x + $y); 3. And another reason they failed: callables are going to want generic types pretty commonly. Think array_filter, array_map, etc. So, I think these brain dump RFCs are all focusing on the wrong problems. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023, at 9:47 PM, David Gebler wrote: > On Thu, Apr 20, 2023 at 6:25 PM Larry Garfield > wrote: > >> ## The options >> >> There's three ways we've come up with that this design could be >> implemented. In concept they're not mutually exclusive, so we could do >> one, two, or three of these. Figuring out which approach would get the >> most support is the purpose of this thread. >> > > My initial feelings based on the options laid out is that anything which > can't support FCCs in the manner of strlen(...) is probably a non-starter > in terms of language design. Changes like this are fundamentally about > making things simpler, more concise and more convenient for users, not > drip-feeding a stream of "and here's yet another way of working with..." > features across releases. > > Structural typing option seems like the easiest to implement in the engine > (correct me if I'm wrong?) and probably the best syntax for the user within > the interface approach. But then do we really want to introduce new runtime > checks and complexity when the general trend of the language has been in > the opposite direction? I imagine probably not. Our assumption is the opposite: Structural typing would be the hardest to implement, and have the largest performance risk, but be the nicest/most convenient for developers. > So out of the three, I lean towards adding castTo() to Closure and it maybe > raises a to-be-determined Throwable if the closure is already bound? It's > not as friendly for the user as the other options but it seems like the > most workable, it delivers value and it most closely fits within the > existing way of working with all types of closure today. > > -Dave --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023 at 6:25 PM Larry Garfield wrote: > ## The options > > There's three ways we've come up with that this design could be > implemented. In concept they're not mutually exclusive, so we could do > one, two, or three of these. Figuring out which approach would get the > most support is the purpose of this thread. > My initial feelings based on the options laid out is that anything which can't support FCCs in the manner of strlen(...) is probably a non-starter in terms of language design. Changes like this are fundamentally about making things simpler, more concise and more convenient for users, not drip-feeding a stream of "and here's yet another way of working with..." features across releases. Structural typing option seems like the easiest to implement in the engine (correct me if I'm wrong?) and probably the best syntax for the user within the interface approach. But then do we really want to introduce new runtime checks and complexity when the general trend of the language has been in the opposite direction? I imagine probably not. So out of the three, I lean towards adding castTo() to Closure and it maybe raises a to-be-determined Throwable if the closure is already bound? It's not as friendly for the user as the other options but it seems like the most workable, it delivers value and it most closely fits within the existing way of working with all types of closure today. -Dave
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023, at 8:26 PM, Michał Marcin Brzuchalski wrote: > Hi > >> There is one loophole, in that an interface may require an __invoke() >> method: >> >> interface TwoInts >> { >> public function __invoke(int $a, int $b): int; >> } >> >> > I was playing around with the code and parser for this in 2020 but my idea > was to introduce a new syntax that is inspired by C# - Delegates [1] > > delegate Reducer (?int $sum, int $item = 0): int; > > class Foo implements Reducer { > public function __invoke(?int $sum, int $item = 0): int { } > } > function reduce(Reducer $reducer) { > var_dump($reducer(0, 5)); > } > reduce(new Foo()); > reduce(fn(?int $sum, int $item = 0): int => 8); > > At the same time, I assumed structural typing for closures would be used. > I assumed the delegate will resolve into > interface Reducer { > public function __invoke(?int $sum, int $item = 0): int {} > } This is effectively the same as the "typedef" version of callable types from my email to Dan a moment ago. See there for the challenges. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
Hi > There is one loophole, in that an interface may require an __invoke() > method: > > interface TwoInts > { > public function __invoke(int $a, int $b): int; > } > > I was playing around with the code and parser for this in 2020 but my idea was to introduce a new syntax that is inspired by C# - Delegates [1] delegate Reducer (?int $sum, int $item = 0): int; class Foo implements Reducer { public function __invoke(?int $sum, int $item = 0): int { } } function reduce(Reducer $reducer) { var_dump($reducer(0, 5)); } reduce(new Foo()); reduce(fn(?int $sum, int $item = 0): int => 8); At the same time, I assumed structural typing for closures would be used. I assumed the delegate will resolve into interface Reducer { public function __invoke(?int $sum, int $item = 0): int {} } I also noticed that once checked closure doesn't have to be checked against the argument types and return type because it won't change which gives some possibility to cache this type check. > The usual discussion has involved a way to specify a callable type's > signature, like so: > > function takeTwo(callable(int $a, int $b): int $c) > { > return $c(1, 2); > } > > But that runs quickly into the problem of verbosity, reusability, and type > aliases, and the discussion usually dies there. > > This is why initially I thought about Delegates as in C# there are not type aliases. The delegate essentially resolves to an interface with `__invoke(?int $sum, int $item = 0): int` method. > ### Structural typing for closures > > The third option would necessitate having similar logic in the engine to > the first. In this case, we take a "structural typing" approach to > closures; that is, "if the types match at runtime, it must be OK." This is > probably closest to the earlier proposals for a `callable(int $x, int $y): > int` syntax (which would by necessity have to be structural), but > essentially uses interfaces as the type alias. > > function takeTwo(TwoInts $c): int > { > return $c(1, 2); > } > > $result = takeTwo(fn(int $x, int $y): int => $x + $y); > I'd love to see this happening. [1] https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/ Cheers, Michał Marcin Brzuchalski
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023, at 7:30 PM, Dan Ackroyd wrote: > On Thu, 20 Apr 2023 at 18:25, Larry Garfield wrote: >> >> Hi folks. This is a pre-discussion, in a sense, before a formal RFC. > > > Hi Larry, > > The "Allow casting closures into single-method interface > implementations" one seems a complete non-starter, as that seems > really hard to work with. You'd have to do lots of "wiring up" to do > any significant amount of programming. That's my concern as well, and part of what led me to suggest the structural typing approach. > "Allow Closures to Declare Interfaces they Implement" > > That sounds bad as it doesn't allow arbitrary functions to be used as > callables. Yes. Which is why I think it would make the most sense when combined with one of the other two options, so it's a sort of performance optimization for the general case. >> We feel that the interface-based approach is strong > > All three of them are using interfaces...? I'm referring to the general idea of this thread, which is "an interface with __invoke is how you define a callable type." Everything else here is a variation on that basic premise. > But if you mean the "Structural Typing for Closures" one, then I'd > probably agree. But as currently proposed it seems like a hack, that > would be predictably regrettable in a couple of years. In what way? >> and a good way forward for getting typed callables >> without a bunch of dependent features needed first. > > Maybe list what you think the dependent features are, so that there > isn't confusion about them, but I suspect that we're going to not > agree on how languages should be designed and evolve. Mainly the type alias question. Every time I see callable types discussed, it immediately sidetracks into "how do we make that less fugly to write, because callable types are naturally very verbose?" That leads directly to typedefs/type aliases, which take one of two forms: type TwoInts = callable(int $x, int $y): int type LinkedResponse = ResponseInterface which raises autoloading questions and means a dependency on a type defined in another package, in many cases. Or: use callable(int $x, int $y): int as TwoInts use ResponseInterface as LinkedResponse Which would be file-local, much like class "use" statements are. That avoids the autoload and dependency problem, at the cost of having to retype that frickin' thing in every file where it's relevant. In many cases, that could be dozens or hundreds of files repeating that line. And that's where the discussion usually dies off. As noted, this is still a form of structural typing, which means the function call process necessarily gets more complex (for callables). Using existing interfaces for callable definitions side steps the implementation challenges of type aliases/typedefs, since once you have an object tagged with an interface (via any of the mechanisms described), its behavior is already very well-defined and predictable. > Although I really want to see typed callables, and other forms of type > aliasing, as they would be huge improvements in being able to write > code that is easy to reason about and maintain, I don't want to seem > them as soon as possible, having taken short-cuts against good > language design. > > "No is temporary, yes is forever". I'm happy to see forward motion on any front. If the result of this thread is that someone gets incentivized to finally figure out callable types for realsies without an interface, I'd sleep happy with that result. But this gives us something concrete to chew on, which we have so far lacked. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
Hi Tim, czw., 20 kwi 2023 o 16:39 Tim Düsterhus napisał(a): > ... > But please no entirely new syntax with braces as it currently is shown > in the examples in the RFC. > Then we should vote for syntax. Personally, I prefer braces here because it doesn't look like a regular function call allowing easily to distinguish between two different things. Cheers, Michał Marcin Brzuchalski
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023, at 7:10 PM, Deleu wrote: > On Thu, Apr 20, 2023 at 2:25 PM Larry Garfield > wrote: > >> But that runs quickly into the problem of verbosity, reusability, and type >> aliases, and the discussion usually dies there. >> > > Just out of curiosity, was there ever a discussion thread dedicated for > Type aliases? I couldn't find it on externals.io and I was curious to know > what are the challenges there since a lot of time and effort seem to have > been put on trying to sidestep it. Not that I recall, on list. There has been discussion on and off in various chat rooms over the last 3-ish years. The general pattern seems to be "that would be cool and useful! But how do we define them, and how does autoloading work for something that's a one liner?" And since no one has a good answer for that, it kinda dies there. I wouldn't expect a formal discussion thread on it until someone has a solid proposal that addresses those questions that they want to run up the flag pole. (Not that I think that's a good process, but that's how things work right now, mostly.) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, 20 Apr 2023 at 18:25, Larry Garfield wrote: > > Hi folks. This is a pre-discussion, in a sense, before a formal RFC. Hi Larry, The "Allow casting closures into single-method interface implementations" one seems a complete non-starter, as that seems really hard to work with. You'd have to do lots of "wiring up" to do any significant amount of programming. "Allow Closures to Declare Interfaces they Implement" That sounds bad as it doesn't allow arbitrary functions to be used as callables. > We feel that the interface-based approach is strong All three of them are using interfaces...? But if you mean the "Structural Typing for Closures" one, then I'd probably agree. But as currently proposed it seems like a hack, that would be predictably regrettable in a couple of years. > and a good way forward for getting typed callables > without a bunch of dependent features needed first. Maybe list what you think the dependent features are, so that there isn't confusion about them, but I suspect that we're going to not agree on how languages should be designed and evolve. Although I really want to see typed callables, and other forms of type aliasing, as they would be huge improvements in being able to write code that is easy to reason about and maintain, I don't want to seem them as soon as possible, having taken short-cuts against good language design. "No is temporary, yes is forever". cheers Dan Ack -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] Callable types via Interfaces
On Thu, Apr 20, 2023 at 2:25 PM Larry Garfield wrote: > But that runs quickly into the problem of verbosity, reusability, and type > aliases, and the discussion usually dies there. > Just out of curiosity, was there ever a discussion thread dedicated for Type aliases? I couldn't find it on externals.io and I was curious to know what are the challenges there since a lot of time and effort seem to have been put on trying to sidestep it. -- Marco Deleu
[PHP-DEV] [Discussion] Callable types via Interfaces
Hi folks. This is a pre-discussion, in a sense, before a formal RFC. Nicolas Grekas and I have been kicking around some ideas for how to address the desire for typed callables, and have several overlapping concepts to consider. Before going down the rabbit hole on any of them we want to gauge the general feeling about the approaches to see what is worth pursuing. We have three "brain dump" RFCs on this topic, although these are all still in super-duper early stages so don't sweat the details in them at this point. We just want to discuss the basic concepts, which I have laid out below. https://wiki.php.net/rfc/allow_casting_closures_into_single-method_interface_implementations https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement https://wiki.php.net/rfc/structural-typing-for-closures ## The problem function takeTwo(callable $c): int { return $c(1, 2); } Right now, we have no way to statically enforce that $c is a callable that takes 2 ints and returns an int. We can document it, but that's it. There is one loophole, in that an interface may require an __invoke() method: interface TwoInts { public function __invoke(int $a, int $b): int; } And then a class may implement TwoInts, and takeTwo() can type against TwoInts. However, that works only for classes, which are naturally considerably more verbose than a simple closure and represent only a subset of the possible callable types. The usual discussion has involved a way to specify a callable type's signature, like so: function takeTwo(callable(int $a, int $b): int $c) { return $c(1, 2); } But that runs quickly into the problem of verbosity, reusability, and type aliases, and the discussion usually dies there. ## The alternative What we propose is to instead lean into the interface approach. Specifically, recall that all closures in PHP are actually implemented as classes in the engine. That is: $f = fn(int $x, int $y): int => $x + $y; actually turns into (approximately) this in the engine: $f = new class extends \Closure { public function __invoke(int $x, int $y): int { return $x + $y; } } (It doesn't do syntax translation but that's effectively what the engine does.) So all that's really missing is a way for arbitrary closures to denote that they implement an interface, and then they can be used wherever an interface is required. That neatly sidesteps the verbosity and reusability issues, and since interfaces are already well-understood there's no need to wait for type aliases. It would not support the old-style funky callables like a function string or [$obj, 'method'], but with the advent of first-class-callables those are no longer recommended anyway so not supporting them is probably a good thing. The same would also work for property types, which can easily type against an interface. That would mostly sidestep the current limitation of typing a property as `callable`, since you could provide a more-specific type instead for a double-win. ## The options There's three ways we've come up with that this design could be implemented. In concept they're not mutually exclusive, so we could do one, two, or three of these. Figuring out which approach would get the most support is the purpose of this thread. ### castTo The first is to add a castTo() method to Closure. That would produce a new object that has the same logic as the closure, but explicitly implements the interface. That is, this: $fn2 = $fn->castTo(TwoInts::class); Is roughly logically equivalent to: $fn2 = new class($fn) implements TwoInts { public function __construct(private callable $fn) {} public function __invoke(int $a, int $b): int { return $this->fn(func_get_args(); } }; (Whether that's what the implementation actually does or if it's smarter about it is an open question.) In theory, this would also support any single-method interface, not just those using __invoke(). The other options below would not support that. This does have a number of open edge cases, like what to do with a closure that is already bound to an object. ### Function interfaces The second option is to allow closures to declare up front what interfaces they implement. So: $f = fn(int $x, int $y): int implements TwoInts => $x + $y; This has the advantage of being more statically analyzable (both visually and for parsers). It may also be more performant (in theory), as it could translate almost trivially to: $f = new class extends \Closure implements TwoInts { public function __invoke(int $x, int $y): int { return $x + $y; } } The downside is that it only works for user-defined closures that declare their support up-front, statically. Something like strlen(...) or strtr(...) wouldn't work. It's also a bit verbose, though using bindTo() directly on the closure is of similar length: $f = (fn(int $x, int $y): int => $x +
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On 20.04.2023 16:15, Larry Garfield wrote: or $point = clone($point, x: $x, y: $y, z: $z); Also, I didn't see it mentioned, but maybe for future scope, these new arguments should be passed to __clone(). They should not. See https://peakd.com/hive-168588/@crell/object-properties-part-2-examples . I went through and experimented with different syntaxes, and passing arguments to __clone() was by far the worst option in practice. :-) Sure, I didn't propose it to be a solution for "clone with", but rather, if we implement "clone with" (no matter in which way) this is "cloning an object with extra properties", so therefore one would ask to have access to these extra properties inside __clone(). I'm not sure how useful that could be, but maybe worth mentioning in future scope or somewhere in the RFC. And I think this syntax should still be on the table, no need for a new keyword. $point = clone($point, x: $x, y: $y, z: $z); -- Aleksander Machniak Kolab Groupware Developer[https://kolab.org] Roundcube Webmail Developer [https://roundcube.net] PGP: 19359DC1 # Blog: https://kolabian.wordpress.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
Hi On 4/20/23 16:17, Larry Garfield wrote: On Thu, Apr 20, 2023, at 7:00 AM, Rowan Tommins wrote: Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking: $point = clone $point with (x: $x, y: $y, z: $z); $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); I agree here, for all the reasons Rowan indicated. We already have a perfectly good syntax and semantics for named arguments that supports splat. Using that here would handle all the use cases we care about, including dynamic names, without any additional syntax. FWIW I'm not too attached to my array proposal [1]. I would also be fine with Rowan's proposal of making the "with()" syntactically identical to a function call, if that's more agreeable. But please no entirely new syntax with braces as it currently is shown in the examples in the RFC. Best regards Tim Düsterhus [1] Though I still consider arrays to be more "natural" than named arguments. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On Thu, Apr 20, 2023, at 7:00 AM, Rowan Tommins wrote: > Rather than making everything use an array or array-like syntax, I > would probably go the other way and scrap the special syntax for > dynamic names, making the whole thing look like a function call, with > support for array unpacking: > > $point = clone $point with (x: $x, y: $y, z: $z); > $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); I agree here, for all the reasons Rowan indicated. We already have a perfectly good syntax and semantics for named arguments that supports splat. Using that here would handle all the use cases we care about, including dynamic names, without any additional syntax. Let's not shoe-horn arrays in here where they're not needed. Making arrays themselves fancier/more compact with an alternate syntax is worth discussing, but that should be a separate RFC. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On Thu, Apr 20, 2023, at 7:22 AM, Aleksander Machniak wrote: > On 20.04.2023 09:00, Rowan Tommins wrote: >> Rather than making everything use an array or array-like syntax, I would >> probably go the other way and scrap the special syntax for dynamic names, >> making the whole thing look like a function call, with support for array >> unpacking: >> >> $point = clone $point with (x: $x, y: $y, z: $z); >> $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); > > or $point = clone($point, x: $x, y: $y, z: $z); > > Also, I didn't see it mentioned, but maybe for future scope, these new > arguments should be passed to __clone(). They should not. See https://peakd.com/hive-168588/@crell/object-properties-part-2-examples . I went through and experimented with different syntaxes, and passing arguments to __clone() was by far the worst option in practice. :-) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On 20/04/2023 08:11, Robert Landers wrote: public function withStatus($code, $reasonPhrase = ''): Response { return clone $this { $this->statusCode = $code; $this->reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; } }; Note that this is not the current proposed syntax. Since the keys are not dynamic, the current proposal is this: public function withStatus($statusCode, $reasonPhrase = ''): Response { // perform validation here $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; return clone $this with {statusCode: $statusCode, reasonPhrase: $reasonPhrase}; } public function withStatus($statusCode, $reasonPhrase = ''): Response { // perform validation here $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; return clone $this with compact('statusCode', 'reasonPhrase'); } The compact() function always feels like a relic of the same era as create_function() and call_user_func(), both of which now have dedicated syntax. That's what Nikita was talking about in the RFC section I quoted earlier: that compact('foo', 'bar') could be replaced with a dedicated syntax like [:$foo, :$bar] So if we insisted on arrays, that would be: public function withStatus($statusCode, $reasonPhrase = ''): Response { // perform validation here $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; return clone $this with [:$statusCode, :$reasonPhrase]; } But I still don't see why an array should be the default case here, rather than using ... to unpack one if you really need to, like we do with arguments. public function withStatus($statusCode, $reasonPhrase = null): Response { $newProps = [:$statusCode]; if ( $reasonPhrase !== null ) { $newProps['reasonPhrase'] = "Old: $this->reasonPhrase, New: $reasonPhrase"; } return clone $this with (...$newProps); } Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On 20.04.2023 09:00, Rowan Tommins wrote: Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking: $point = clone $point with (x: $x, y: $y, z: $z); $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); or $point = clone($point, x: $x, y: $y, z: $z); Also, I didn't see it mentioned, but maybe for future scope, these new arguments should be passed to __clone(). -- Aleksander Machniak Kolab Groupware Developer[https://kolab.org] Roundcube Webmail Developer [https://roundcube.net] PGP: 19359DC1 # Blog: https://kolabian.wordpress.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On Thu, Apr 20, 2023 at 9:01 AM Rowan Tommins wrote: > > On 18 April 2023 17:48:00 BST, "Tim Düsterhus" wrote: > >I'd rather see only the fat-arrow being allowed. Unless I'm missing > >something, braces with colon is not used anywhere else, whereas braces + > >'=>' is known from match() and '=>' more generally is already used with > >array literals [1]. Having two similar syntaxes for the same thing is not > >great when both are commonly needed is not great. They need to be documented > >and learned by developers. > > > I think it makes sense to have an unquoted form here, because the common case > is that they are names which analysers can match statically to particular > properties, not strings which will be analysed at runtime. There are plenty > of places in the language where dynamic names are allowed, but we don't just > use strings for the static case: > > ${'foo'} = 'bar'( constant('BAZ') )->{'quux'}(); > > > More specifically, the "name: $value" syntax matches named parameters, and > while you can use an array for that (via ... unpacking), we don't force users > to do so. > > In fact, the Future Scope of that RFC considered the opposite: using the > colon syntax in arrays, along with a shorthand for pulling the key name from > the variable name: > https://wiki.php.net/rfc/named_params#shorthand_syntax_for_matching_parameter_and_variable_name > > > If I wanted to put these ideas into a general framework, I think one way > > to go about this would be as follows: > > > > - Consider identifier: $expr as a shorthand for "identifier" => $expr. > > - Consider :$variable as a shorthand for variable: $variable and thus > "variable" => $variable. > > > That would give us: > > $point = ['x' => $x, 'y' => $y, 'z' => $z]; > $point = [x: $x, y: $y, z: $z]; > $point = [:$x, :$y, :$z]; > > $point = new Point(...['x' => $x, 'y' => $y, 'z' => $z]); > $point = new Point(x: $x, y: $y, z: $z); > $point = new Point(:$x, :$y, :$z); > > $point = clone $point with {'x' => $x, 'y' => $y, 'z' => $z}; > $point = clone $point with {x: $x, y: $y, z: $z}; > $point = clone $point with {:$x, :$y, :$z}; > > > > Rather than making everything use an array or array-like syntax, I would > probably go the other way and scrap the special syntax for dynamic names, > making the whole thing look like a function call, with support for array > unpacking: > > $point = clone $point with (x: $x, y: $y, z: $z); > $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); > > > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > I think using arrays here makes a lot of sense. For example, this example: public function withStatus($code, $reasonPhrase = ''): Response { return clone $this { $this->statusCode = $code; $this->reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; } }; could be rewritten with public function withStatus($statusCode, $reasonPhrase = ''): Response { // perform validation here $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase"; return clone $this with compact('statusCode', 'reasonPhrase'); } I, personally, would find this much more ergonomic than writing out blocks of code and having a totally different syntax. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] [Discussion] Clone with
On 18 April 2023 17:48:00 BST, "Tim Düsterhus" wrote: >I'd rather see only the fat-arrow being allowed. Unless I'm missing something, >braces with colon is not used anywhere else, whereas braces + '=>' is known >from match() and '=>' more generally is already used with array literals [1]. >Having two similar syntaxes for the same thing is not great when both are >commonly needed is not great. They need to be documented and learned by >developers. I think it makes sense to have an unquoted form here, because the common case is that they are names which analysers can match statically to particular properties, not strings which will be analysed at runtime. There are plenty of places in the language where dynamic names are allowed, but we don't just use strings for the static case: ${'foo'} = 'bar'( constant('BAZ') )->{'quux'}(); More specifically, the "name: $value" syntax matches named parameters, and while you can use an array for that (via ... unpacking), we don't force users to do so. In fact, the Future Scope of that RFC considered the opposite: using the colon syntax in arrays, along with a shorthand for pulling the key name from the variable name: https://wiki.php.net/rfc/named_params#shorthand_syntax_for_matching_parameter_and_variable_name > If I wanted to put these ideas into a general framework, I think one way to > go about this would be as follows: > > - Consider identifier: $expr as a shorthand for "identifier" => $expr. > - Consider :$variable as a shorthand for variable: $variable and thus > "variable" => $variable. That would give us: $point = ['x' => $x, 'y' => $y, 'z' => $z]; $point = [x: $x, y: $y, z: $z]; $point = [:$x, :$y, :$z]; $point = new Point(...['x' => $x, 'y' => $y, 'z' => $z]); $point = new Point(x: $x, y: $y, z: $z); $point = new Point(:$x, :$y, :$z); $point = clone $point with {'x' => $x, 'y' => $y, 'z' => $z}; $point = clone $point with {x: $x, y: $y, z: $z}; $point = clone $point with {:$x, :$y, :$z}; Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking: $point = clone $point with (x: $x, y: $y, z: $z); $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]); Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php