Re: [PHP-DEV] [RFC] First-class callable syntax
2021-05-20 14:48 GMT+02:00, Nikita Popov : > Hi internals, > > I'd like to present an RFC for a first-class callable syntax, which is > intended as a simpler alternative to the partial function application (PFA) > proposal: > > https://wiki.php.net/rfc/first_class_callable_syntax > > See the Rationale section for details on how this relates to PFA. Over the > past week, we've had a lot of discussions on how exactly PFA is supposed to > work (most of them OTR), and while we seem to have a tentative consensus on > the correct model to use (which does not match the current RFC), it's clear > that this feature has turned out more complicated than originally > anticipated. Joe (who provided the implementation for the PFA RFC) is also > concerned about the implementation complexity that the final model would > require. > > At least I personally was mainly interested in PFA because it provides a > first-class callable syntax as a side-effect. This RFC goes back to > providing *just* that. The syntax is forward-compatible with a future PFA > proposal though. > > Regards, > Nikita More bikeshedding, sorry. Was a syntax using `fn` considered? Like $fn = Closure::fromCallable('strlen'); $fn = fn strlen; or $fn = (fn) strlen; Olle -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
On Fri, May 21, 2021 at 10:08 AM Rowan Tommins wrote: > > Everyone: please let's keep this thread for talking about first-class > callables, and focus on the semantics not just the syntax - are there > edge cases we need to consider, downsides to the proposed > implementation, etc? > Well the semantics seem rather clear? We could say one "downside" is that new-expressions are not supported, but the RFC explains why ( https://wiki.php.net/rfc/first_class_callable_syntax#object_creation, I think it makes sense), and special-casing can always be added later (or in PFA). Regards, -- Guilliam Xavier
Re: [PHP-DEV] [RFC] First-class callable syntax
On Fri, May 21, 2021 at 11:09 AM Andreas Leathley wrote: > [...] > > About the bikeshedding: Using "..." as a symbol does make sense to me, > as variadic unpacking/variadic arguments have a similar connotation > (referring to an unknown/arbitrary amount of elements). * was also > suggested (as in "somefunction(*)" instead of "somefunction(...)"), and > I like that too - * in regex is any amount of elements (even zero), and > ? is exactly one placeholder in SQL. As many PHP developers know regex > and SQL this would make PFA code a lot easier to understand even if > someone is not that familiar with it yet. > FWIW: In this context, ? and * remind me of shell wildcards (used by glob() and fnmatch()), which makes sense too. Also, at least one person is opposed to "...". -- Guilliam Xavier
Re: [PHP-DEV] [RFC] First-class callable syntax
Le ven. 21 mai 2021 à 16:31, Larry Garfield a écrit : > On Fri, May 21, 2021, at 2:52 AM, Nicolas Grekas wrote: > > Sorry for self-reply, this needs some clarifications :) > > > > Le ven. 21 mai 2021 à 09:17, Nicolas Grekas < > nicolas.grekas+...@gmail.com> > > a écrit : > > > > >> There's been a lot of rapid iteration, experimentation, and rejection. > > >> The most recent alternatives are this one from Levi: > > >> > > >> https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd > > >> > > >> And this one from me: > > >> > > >> https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 > > >> > > >> The main takeaways (to give context to Nikita's proposal): > > >> > > >> * Because of optional arguments, using the same symbol for "copy one > > >> parameter from the underlying function" and "copy all remaining > parameters > > >> from the underlying function" is not viable. It runs into oddball > cases > > >> where you may intend to only use one argument but end up using > multiple, > > >> especially in array operation functions that sometimes silently pass > keys > > >> along to callbacks if they can. Hence the separate ? and ... that > were > > >> proposed. > > >> > > > > > > I've read that some ppl think this should be fought, but that's > actually a > > > critical feature of the engine when writing BC layers by adding extra > > > arguments via this possibility. > > > > > > > I'm talking about implicit extra arguments accessed via func_get_args() > > here. > > The issue is with optional arguments that would get auto-forwarded, even > when you don't want to use them. (It wasn't obvious to me for a long time > that this issue existed either.) The classic example, ported from > Javascript: > > function printInt(string $value, int $base = 10): void > { > echo intval($value, $base), "\n"; > } > > $array = ['10', '10', '10', '10', '10']; > > array_walk($array, 'printInt'); > > Results in: > > 10 > 0 > 2 > 3 > 4 > > The function has multiple arguments, but we only want to use one, but > functions like array_walk() will try to use the optional one if they > can... even if doing so makes no logical sense. Having ? capture all > arguments unconditionally would also mean it captures optional arguments > even if you don't want it to. That's why we need to have the separate ... > symbol for when you really do mean "and all the rest". > Got it thanks. Makes sense to me now. In this case, we need printInt(?) to only pass $value through. If you > really did want to use the array key for the base for some reason, you > could do printInt(?, ?) or printInt(...). > > I think the plan would be that extra passed arguments to a partialed > function would still forward through, but that may end up being more > difficult than expected. > > > In the 2nd gist, I read "If the current position is a ..., copy all > > > remaining parameters from the underlying function, including a > variadic." > > > But to me it's important that all extra arguments are forwarded to the > > > partialized callable, where ... is used or not. > > > Please clarify this point if possible. > > I think this is the same as the previous question? > > (Note that several vocal people have argued that the way PHP passes along > extraneous arguments is a bug, not a feature. I don't have a strong > feeling about it, but some do.) > Being vocal doesn't mean being right :) This feature of the language is critical to write BC layers, see eg this, LSP rulez: https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php#L38 It has to stay (unless an alternative is provided of course.) > > * Named arguments make things more complicated. One of the questions is > > >> whether named placeholders should be supported or not. And if they > are, > > >> does that mean you can effectively reorder the arguments in the > partial > > >> application, and what does that mean for usability. It gets > complicated > > >> and scope-creepy fast. > > >> > > > > > > For the userland pov, the principle of least surprise would say we > should > > > answer yes to both questions. > > > > Reading your gists, I'm with drawing this expectation. > > > > Since calls reorder named arguments automatically, we could expect the > same > > for PFAs. > > That is: > > since foo(A: 1, B: 2) is exactly the same as foo(B: 2, A: 1) > > we could also expect that foo(A: ?, B: ?) would be exactly the same > > foo(B:?, A:?) > > > > The issue here is that the syntax can be interpreted as both a new > > signature and as a partial call. > > If we stay pure to the intend of PFAs, argument reordering shouldn't be > > allowed IMHO. Aka this should be interpreted only as a partial call. > > > > *If* we need a way to reorder arguments, I think it would be natural also > > to be able to *rename* arguments. > > Which is also where the rabbit hole gets extra deep, and why I am > perfectly happy to just disallow named
Re: [PHP-DEV] [RFC] First-class callable syntax
On Fri, May 21, 2021, at 2:52 AM, Nicolas Grekas wrote: > Sorry for self-reply, this needs some clarifications :) > > Le ven. 21 mai 2021 à 09:17, Nicolas Grekas > a écrit : > >> There's been a lot of rapid iteration, experimentation, and rejection. > >> The most recent alternatives are this one from Levi: > >> > >> https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd > >> > >> And this one from me: > >> > >> https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 > >> > >> The main takeaways (to give context to Nikita's proposal): > >> > >> * Because of optional arguments, using the same symbol for "copy one > >> parameter from the underlying function" and "copy all remaining parameters > >> from the underlying function" is not viable. It runs into oddball cases > >> where you may intend to only use one argument but end up using multiple, > >> especially in array operation functions that sometimes silently pass keys > >> along to callbacks if they can. Hence the separate ? and ... that were > >> proposed. > >> > > > > I've read that some ppl think this should be fought, but that's actually a > > critical feature of the engine when writing BC layers by adding extra > > arguments via this possibility. > > > > I'm talking about implicit extra arguments accessed via func_get_args() > here. The issue is with optional arguments that would get auto-forwarded, even when you don't want to use them. (It wasn't obvious to me for a long time that this issue existed either.) The classic example, ported from Javascript: function printInt(string $value, int $base = 10): void { echo intval($value, $base), "\n"; } $array = ['10', '10', '10', '10', '10']; array_walk($array, 'printInt'); Results in: 10 0 2 3 4 The function has multiple arguments, but we only want to use one, but functions like array_walk() will try to use the optional one if they can... even if doing so makes no logical sense. Having ? capture all arguments unconditionally would also mean it captures optional arguments even if you don't want it to. That's why we need to have the separate ... symbol for when you really do mean "and all the rest". In this case, we need printInt(?) to only pass $value through. If you really did want to use the array key for the base for some reason, you could do printInt(?, ?) or printInt(...). I think the plan would be that extra passed arguments to a partialed function would still forward through, but that may end up being more difficult than expected. > In the 2nd gist, I read "If the current position is a ..., copy all > > remaining parameters from the underlying function, including a variadic." > > But to me it's important that all extra arguments are forwarded to the > > partialized callable, where ... is used or not. > > Please clarify this point if possible. I think this is the same as the previous question? (Note that several vocal people have argued that the way PHP passes along extraneous arguments is a bug, not a feature. I don't have a strong feeling about it, but some do.) > > * Named arguments make things more complicated. One of the questions is > >> whether named placeholders should be supported or not. And if they are, > >> does that mean you can effectively reorder the arguments in the partial > >> application, and what does that mean for usability. It gets complicated > >> and scope-creepy fast. > >> > > > > For the userland pov, the principle of least surprise would say we should > > answer yes to both questions. > > Reading your gists, I'm with drawing this expectation. > > Since calls reorder named arguments automatically, we could expect the same > for PFAs. > That is: > since foo(A: 1, B: 2) is exactly the same as foo(B: 2, A: 1) > we could also expect that foo(A: ?, B: ?) would be exactly the same > foo(B:?, A:?) > > The issue here is that the syntax can be interpreted as both a new > signature and as a partial call. > If we stay pure to the intend of PFAs, argument reordering shouldn't be > allowed IMHO. Aka this should be interpreted only as a partial call. > > *If* we need a way to reorder arguments, I think it would be natural also > to be able to *rename* arguments. Which is also where the rabbit hole gets extra deep, and why I am perfectly happy to just disallow named placeholders. In the rare use cases where you really do want/need to reorder arguments and do other esoteric things, short lambdas already let you do that much more explicitly. That's an edge case that already has a solution. We're focused here on making the most common cases nicer. > That's where my syntax proposal based on short closures might be handy: > > fn($a, $b, ...) => $callable($b, a: $a, ...) At that point we're not getting any benefit over just using arrow functions. The point of PFA is that it's partially calling a function as-is, in a compact way. That in PHP we'd be implementing it as yet-another-closure object is an
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20.05.21 21:35, Larry Garfield wrote: There's been a lot of rapid iteration, experimentation, and rejection. The most recent alternatives are this one from Levi: https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd And this one from me: https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 The main takeaways (to give context to Nikita's proposal): * Because of optional arguments, using the same symbol for "copy one parameter from the underlying function" and "copy all remaining parameters from the underlying function" is not viable. It runs into oddball cases where you may intend to only use one argument but end up using multiple, especially in array operation functions that sometimes silently pass keys along to callbacks if they can. Hence the separate ? and ... that were proposed. * Named arguments make things more complicated. One of the questions is whether named placeholders should be supported or not. And if they are, does that mean you can effectively reorder the arguments in the partial application, and what does that mean for usability. It gets complicated and scope-creepy fast. * The most important use cases are: ** "prefill nothing, just give me a callable" (which is the case Nikita's RFC covers) ** "reduce an arbitrary function to a single remaining argument", which lets it be used by various existing callback functions in the standard library (array_map, array_filter, etc.), many user-space APIs, and the pipes RFC that I am planning to bring back up once PFA is figured out (https://wiki.php.net/rfc/pipe-operator-v2). While there are other use cases, the two of those cover the vast majority of uses. (There is some dispute about which of those is larger, or will be larger in the future.) It took a while to realize the first point, which basically killed "? means zero or more". We also went down a rabbit hole of trying to make argument reordering work because some people asked for it, but as noted that's a very deep rabbit hole. My gist above was a reduced-scope version of Levi's that uses two symbols and drops named placeholders. It doesn't give us every possible combination, but it does give us most reasonable combinations. Nikita's "just do the first one" RFC (this thread) was proposed at about the same time, and takes an even-further scope reduction. My own take is that the PFA discussion has been overly-bikeshedded, which is unfortunate since I think we're quite close to now having a workable answer that covers most reasonable use cases. While I agree Nikita's RFC here would be an improvement over 8.0, I don't think throwing in the towel on PFA yet is a good idea. It's a much more robust and powerful approach that still gets us the "first class callable" syntax we all want (at least I assume we all do), and lots of additional power to boot. I'd rather see us try to drive PFA home to completion. If that proves impossible by early July, then this RFC would still get us something this cycle, as long as the syntax is still compatible with PFA. (Otherwise whenever PFA gets sorted out in the future we end up with yet-more-ways to do the same thing that are not optimizations of each other but just competing syntax, in which case no one wins.) I think Levis proposal and the two symbols ... and ? are a really good improvement to the previous PFA RFC (Nikitas/Joes RFC is a good first step in that direction). I do think named arguments are important, but reordering arguments could be confusing and are not necessary (and if named arguments are used for the PFA, it would make sense to use named arguments for calling it too). Another big reason for me why I like Levis proposal more is that he still includes partially applying the new operator. Although the semantics of the new operator are a bit different from regular functions, it would be very handy to support this special case. array_map is an obvious application: $productObjects = array_map(new Product(?, ?, ?), $productIds, $currencies, $prices); But other usages would probably pop up, like abstracting away dependencies of classes by pre-setting those via PFA, like: $productFactory = new Product($dependency1, $dependency2, ...); $productObjects = array_map($productFactory, $productIds, $currencies, $prices); I really like this because from a users perspective this seems very clear and readable to me, even though from the PHP language perspective it might be more messy. About the bikeshedding: Using "..." as a symbol does make sense to me, as variadic unpacking/variadic arguments have a similar connotation (referring to an unknown/arbitrary amount of elements). * was also suggested (as in "somefunction(*)" instead of "somefunction(...)"), and I like that too - * in regex is any amount of elements (even zero), and ? is exactly one placeholder in SQL. As many PHP developers know regex and SQL this would make PFA code a lot easier to understand even if someone is
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20/05/2021 23:58, David Gebler wrote: I think this is very sensible, I can only really say I'd rather have Nikita's proposal land in 8.1 and PFAs in 9.0 done right than have PFAs in 8.1 but in a way which is confusing, ambiguous or problematic for users, or not covering reasonable expected use cases. I wholeheartedly agree with this sentiment. I hope it doesn't take until 9.0, but if we get first-class callables in 8.1 and partial application in 8.2 those are BOTH huge new features. And if by some miracle we come up with the perfect version of PFA a week after this RFC passes, that's ALSO great. Everyone: please let's keep this thread for talking about first-class callables, and focus on the semantics not just the syntax - are there edge cases we need to consider, downsides to the proposed implementation, etc? Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
Sorry for self-reply, this needs some clarifications :) Le ven. 21 mai 2021 à 09:17, Nicolas Grekas a écrit : > Thank you all for your efforts, I think we're almost there and that PFA > would be a really great and useful addition to the language. > > Le jeu. 20 mai 2021 à 21:38, Larry Garfield a > écrit : > >> On Thu, May 20, 2021, at 10:55 AM, Guilliam Xavier wrote: >> > On Thu, May 20, 2021 at 5:12 PM Nikita Popov >> wrote: >> > >> > > On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes >> wrote: >> > > >> > > > Hi, I’m confused by the syntax, when I read it, I think to myself >> “I know >> > > > this, this is just a method call… oh wait, it’s actually a >> callable”. It >> > > > really makes my head hurt. >> > > > >> > > >> > > Yes, I can see how that could be confusing. The current syntax is >> chosen to >> > > be a subset of the partial function application proposal. However, I >> would >> > > also be happy with some other syntax that makes it clearer that this >> is >> > > acquiring a callable and not performing a call. >> > > >> > >> > Hi, several other syntaxes have been proposed to consideration in the >> PFA >> > thread, and I wouldn't want to start new bikeshedding here; is there a >> > place that would be more appropriate to gather the possibilities (like a >> > kind of updatable list)? >> > >> > Thanks, >> >> There's been a lot of rapid iteration, experimentation, and rejection. >> The most recent alternatives are this one from Levi: >> >> https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd >> >> And this one from me: >> >> https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 >> >> The main takeaways (to give context to Nikita's proposal): >> >> * Because of optional arguments, using the same symbol for "copy one >> parameter from the underlying function" and "copy all remaining parameters >> from the underlying function" is not viable. It runs into oddball cases >> where you may intend to only use one argument but end up using multiple, >> especially in array operation functions that sometimes silently pass keys >> along to callbacks if they can. Hence the separate ? and ... that were >> proposed. >> > > I've read that some ppl think this should be fought, but that's actually a > critical feature of the engine when writing BC layers by adding extra > arguments via this possibility. > I'm talking about implicit extra arguments accessed via func_get_args() here. In the 2nd gist, I read "If the current position is a ..., copy all > remaining parameters from the underlying function, including a variadic." > But to me it's important that all extra arguments are forwarded to the > partialized callable, where ... is used or not. > Please clarify this point if possible. > > * Named arguments make things more complicated. One of the questions is >> whether named placeholders should be supported or not. And if they are, >> does that mean you can effectively reorder the arguments in the partial >> application, and what does that mean for usability. It gets complicated >> and scope-creepy fast. >> > > For the userland pov, the principle of least surprise would say we should > answer yes to both questions. > Reading your gists, I'm with drawing this expectation. Since calls reorder named arguments automatically, we could expect the same for PFAs. That is: since foo(A: 1, B: 2) is exactly the same as foo(B: 2, A: 1) we could also expect that foo(A: ?, B: ?) would be exactly the same foo(B:?, A:?) The issue here is that the syntax can be interpreted as both a new signature and as a partial call. If we stay pure to the intend of PFAs, argument reordering shouldn't be allowed IMHO. Aka this should be interpreted only as a partial call. *If* we need a way to reorder arguments, I think it would be natural also to be able to *rename* arguments. That's where my syntax proposal based on short closures might be handy: fn($a, $b, ...) => $callable($b, a: $a, ...) > > >> We also went down a rabbit hole of trying to make argument reordering >> work because some people asked for it, but as noted that's a very deep >> rabbit hole. >> > > Then maybe named args should be forbidden for "?" placeholders. This would > keep this possibility open of the future and close the "principle of least > surprise" expectation if there are. Aka foo(bar: ?) should be a parse error > for now at least. > > My own take is that the PFA discussion has been overly-bikeshedded, which >> is unfortunate since I think we're quite close to now having a workable >> answer that covers most reasonable use cases. While I agree Nikita's RFC >> here would be an improvement over 8.0, I don't think throwing in the towel >> on PFA yet is a good idea. It's a much more robust and powerful approach >> that still gets us the "first class callable" syntax we all want (at least >> I assume we all do), and lots of additional power to boot. I'd rather see >> us try to drive PFA home to completion. If that proves
Re: [PHP-DEV] [RFC] First-class callable syntax
Thank you all for your efforts, I think we're almost there and that PFA would be a really great and useful addition to the language. Le jeu. 20 mai 2021 à 21:38, Larry Garfield a écrit : > On Thu, May 20, 2021, at 10:55 AM, Guilliam Xavier wrote: > > On Thu, May 20, 2021 at 5:12 PM Nikita Popov > wrote: > > > > > On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes > wrote: > > > > > > > Hi, I’m confused by the syntax, when I read it, I think to myself “I > know > > > > this, this is just a method call… oh wait, it’s actually a > callable”. It > > > > really makes my head hurt. > > > > > > > > > > Yes, I can see how that could be confusing. The current syntax is > chosen to > > > be a subset of the partial function application proposal. However, I > would > > > also be happy with some other syntax that makes it clearer that this is > > > acquiring a callable and not performing a call. > > > > > > > Hi, several other syntaxes have been proposed to consideration in the PFA > > thread, and I wouldn't want to start new bikeshedding here; is there a > > place that would be more appropriate to gather the possibilities (like a > > kind of updatable list)? > > > > Thanks, > > There's been a lot of rapid iteration, experimentation, and rejection. > The most recent alternatives are this one from Levi: > > https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd > > And this one from me: > > https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 > > The main takeaways (to give context to Nikita's proposal): > > * Because of optional arguments, using the same symbol for "copy one > parameter from the underlying function" and "copy all remaining parameters > from the underlying function" is not viable. It runs into oddball cases > where you may intend to only use one argument but end up using multiple, > especially in array operation functions that sometimes silently pass keys > along to callbacks if they can. Hence the separate ? and ... that were > proposed. > I've read that some ppl think this should be fought, but that's actually a critical feature of the engine when writing BC layers by adding extra arguments via this possibility. In the 2nd gist, I read "If the current position is a ..., copy all remaining parameters from the underlying function, including a variadic." But to me it's important that all extra arguments are forwarded to the partialized callable, where ... is used or not. Please clarify this point if possible. * Named arguments make things more complicated. One of the questions is > whether named placeholders should be supported or not. And if they are, > does that mean you can effectively reorder the arguments in the partial > application, and what does that mean for usability. It gets complicated > and scope-creepy fast. > For the userland pov, the principle of least surprise would say we should answer yes to both questions. > We also went down a rabbit hole of trying to make argument reordering work > because some people asked for it, but as noted that's a very deep rabbit > hole. > Then maybe named args should be forbidden for "?" placeholders. This would keep this possibility open of the future and close the "principle of least surprise" expectation if there are. Aka foo(bar: ?) should be a parse error for now at least. My own take is that the PFA discussion has been overly-bikeshedded, which > is unfortunate since I think we're quite close to now having a workable > answer that covers most reasonable use cases. While I agree Nikita's RFC > here would be an improvement over 8.0, I don't think throwing in the towel > on PFA yet is a good idea. It's a much more robust and powerful approach > that still gets us the "first class callable" syntax we all want (at least > I assume we all do), and lots of additional power to boot. I'd rather see > us try to drive PFA home to completion. If that proves impossible by early > July, then this RFC would still get us something this cycle, as long as the > syntax is still compatible with PFA. (Otherwise whenever PFA gets sorted > out in the future we end up with yet-more-ways to do the same thing that > are not optimizations of each other but just competing syntax, in which > case no one wins.) > I agree. Let's figure out PFAs! If I may add to the bikeshedding, and since PFAs are like others said "short lambas on steroids", what about borrowing the syntax from them? Here would be the first-class callable syntax: fn(...) => $callable(...) and for PFAs: fn(...) => $callable(?, 42, ...) Nicolas
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, May 20, 2021 at 8:38 PM Larry Garfield wrote: > > There's been a lot of rapid iteration, experimentation, and rejection. > The most recent alternatives are this one from Levi: > > https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd > > And this one from me: > > https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 > > Intuitively, I think I prefer your algorithm as these are written up. Levi's allows for named placeholders which I'm sure for some users is a big draw. But I think what you're proposing is cleaner and will be better understood. It resolves a lot of the concerns which came up in the PFA thread, striking a reasonable balance between benefit and simplicity. Either way suggests compatibility with Nikita's proposal, assuming use of the spread operator as the implemented syntax. For this RFC in isolation, though, it does loosely concern me as a user the proposed syntax looks more like a function call. I won't bog anyone down arguing the syntax, it's a minor detail for users to get used to in the grand scheme of things and I'd definitely rather it was possible to build the more powerful PFA on top than bin that off, or end up with a convoluted PHP providing competing ways of doing the same thing. > * Named arguments make things more complicated. One of the questions is > whether named placeholders should be supported or not. And if they are, > does that mean you can effectively reorder the arguments in the partial > application, and what does that mean for usability. It gets complicated > and scope-creepy fast. > I do share the concern about named arguments and it's one of the reasons I prefer your proposal. I think scaling back the scope a little bit is exactly what's needed right now. > While I agree Nikita's RFC here would be an improvement over 8.0, I don't > think throwing in the towel on PFA yet is a good idea. It's a much more > robust and powerful approach that still gets us the "first class callable" > syntax we all want (at least I assume we all do), and lots of additional > power to boot. I'd rather see us try to drive PFA home to completion. If > that proves impossible by early July, then this RFC would still get us > something this cycle, as long as the syntax is still compatible with PFA. > I think this is very sensible, I can only really say I'd rather have Nikita's proposal land in 8.1 and PFAs in 9.0 done right than have PFAs in 8.1 but in a way which is confusing, ambiguous or problematic for users, or not covering reasonable expected use cases.
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20/05/2021 21:54, Kamil Tekiela wrote: This would be less confusing than the (...) syntax IMHO. Of course this still has the same ambiguity as Rowan points out. Is ::$objA->methA a property or a method? We could solve this problem by specifying the syntax to always refer to methods/functions. Parentheses could be used to enforce access to property, e.g. ::($objA->methA) would evaluate the value stored in the public property of the object $objA This is precisely the kind of "solution" that I referred to as easy for the parser but confusing for humans. ::$objA->methA // method (::$objA->methA) // method ::($objA->methA) // property ::($objA)->methA // method? (::$objA)->methA // attempt to access a property of a Closure object? It's just a mess, IMHO Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
Hi Nikita, I would like to just express my feelings. It's a definite YES from me for first-class callables. We need something to replace [$this, 'privateMethod']. I just don't like the proposed syntax. The triple period '...' has a meaning already and reusing the same syntax isn't nice. I haven't followed the PFA RFC closely, but I don't like that proposal as it seems to add additional complexity to PHP's syntax for little gain. I feel like it's just arrow function on steroids. I know we can already do something like this: function do_stuff($e) { return $e**2; } $result = array_map(do_stuff::class, [1,2,3,4]); I wouldn't mind having a dedicated operator for creating closures in the same way. e.g. $result = array_map(do_stuff::closure, [1,2,3,4]); I understand that ::class creates a string literal and ::closure creates closure, but from the usability point of view, they would be used in similar ways. Many languages have shared symbol tables for constants, functions and variables. PHP doesn't, so we can't just use the name, but I agree with Rowan that just using a function's name would be ambiguous even if the parser allowed for that. We could introduce new syntax for this. I think Kotlin uses double semicolon in front of the identifier. e.g. $result = array_map(::do_stuff, [1,2,3,4]); This would be less confusing than the (...) syntax IMHO. Of course this still has the same ambiguity as Rowan points out. Is ::$objA->methA a property or a method? We could solve this problem by specifying the syntax to always refer to methods/functions. Parentheses could be used to enforce access to property, e.g. ::($objA->methA) would evaluate the value stored in the public property of the object $objA If we land on using the syntax as your RFC proposes then I would ask Internals to avoid (...) and instead use another symbol inside the parentheses. I believe any other symbol would look better than triple period, e.g. $objA->methA(*) Kind Regards, Kamil
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20/05/2021 19:16, Alexandru Pătrănescu wrote: Also, considering the resolution between property and method (or between constants and static methods) , it's clear that we need a syntax that looks like the usual invocation:(...), or (?), (...?), ($), ($$), (...$) etc. I'd like to expand on this point, because I'm seeing a lot of suggestions for syntaxes that overlook it. The following are all valid, but refer to _different_ things called "foo": foo // a constant called foo foo() // a function called foo $foo // a variable called foo $bar->foo // a property called foo $bar->foo() // a method called foo Bar::foo // a class constant Bar::foo() // a static method Bar::$foo // a static property The thing that consistently distinguishes function and method calls is the parentheses after them. This holds even for "exotic" combinations: $foo() // a variable called foo, de-referenced as a callable and then invoked ($bar->foo)() // a property called foo, de-referenced as a callable and then invoked foo()() // a function called foo, the return value of which is de-referenced as a callable and invoked Any callable syntax (and, if/when we get one, a partial application syntax) that doesn't have the parentheses there is going to run into annoying edge cases fast. Not necessarily the kind of edge cases that are hard for the parser, but definitely the kind that are confusing to humans. Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, May 20, 2021, at 10:55 AM, Guilliam Xavier wrote: > On Thu, May 20, 2021 at 5:12 PM Nikita Popov wrote: > > > On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes wrote: > > > > > Hi, I’m confused by the syntax, when I read it, I think to myself “I know > > > this, this is just a method call… oh wait, it’s actually a callable”. It > > > really makes my head hurt. > > > > > > > Yes, I can see how that could be confusing. The current syntax is chosen to > > be a subset of the partial function application proposal. However, I would > > also be happy with some other syntax that makes it clearer that this is > > acquiring a callable and not performing a call. > > > > Hi, several other syntaxes have been proposed to consideration in the PFA > thread, and I wouldn't want to start new bikeshedding here; is there a > place that would be more appropriate to gather the possibilities (like a > kind of updatable list)? > > Thanks, There's been a lot of rapid iteration, experimentation, and rejection. The most recent alternatives are this one from Levi: https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbfd And this one from me: https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 The main takeaways (to give context to Nikita's proposal): * Because of optional arguments, using the same symbol for "copy one parameter from the underlying function" and "copy all remaining parameters from the underlying function" is not viable. It runs into oddball cases where you may intend to only use one argument but end up using multiple, especially in array operation functions that sometimes silently pass keys along to callbacks if they can. Hence the separate ? and ... that were proposed. * Named arguments make things more complicated. One of the questions is whether named placeholders should be supported or not. And if they are, does that mean you can effectively reorder the arguments in the partial application, and what does that mean for usability. It gets complicated and scope-creepy fast. * The most important use cases are: ** "prefill nothing, just give me a callable" (which is the case Nikita's RFC covers) ** "reduce an arbitrary function to a single remaining argument", which lets it be used by various existing callback functions in the standard library (array_map, array_filter, etc.), many user-space APIs, and the pipes RFC that I am planning to bring back up once PFA is figured out (https://wiki.php.net/rfc/pipe-operator-v2). While there are other use cases, the two of those cover the vast majority of uses. (There is some dispute about which of those is larger, or will be larger in the future.) It took a while to realize the first point, which basically killed "? means zero or more". We also went down a rabbit hole of trying to make argument reordering work because some people asked for it, but as noted that's a very deep rabbit hole. My gist above was a reduced-scope version of Levi's that uses two symbols and drops named placeholders. It doesn't give us every possible combination, but it does give us most reasonable combinations. Nikita's "just do the first one" RFC (this thread) was proposed at about the same time, and takes an even-further scope reduction. My own take is that the PFA discussion has been overly-bikeshedded, which is unfortunate since I think we're quite close to now having a workable answer that covers most reasonable use cases. While I agree Nikita's RFC here would be an improvement over 8.0, I don't think throwing in the towel on PFA yet is a good idea. It's a much more robust and powerful approach that still gets us the "first class callable" syntax we all want (at least I assume we all do), and lots of additional power to boot. I'd rather see us try to drive PFA home to completion. If that proves impossible by early July, then this RFC would still get us something this cycle, as long as the syntax is still compatible with PFA. (Otherwise whenever PFA gets sorted out in the future we end up with yet-more-ways to do the same thing that are not optimizations of each other but just competing syntax, in which case no one wins.) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20.05.2021 19:05, Nikita Popov wrote: >> $fn = ::myFunc; >> > Unfortunately, this syntax is trivially ambiguous. "$fn = &$this->myFunc" > is currently already interpreted as a reference assignment of the property > $this->myFunc. could that be just fn(sth)? I mean without the => part. $fn = fn($this->myFunc); $fn = fn(myFunc); $fn = fn(Foo::myFunc); and then something like this $partial = fn(Foo::myFunc)[?, 42]; or $partial = fn(Foo::myFunc, ?, 42); My earlier idea was that actually cloning a callable could convert it to a Closure, but I'm not sure now: $fn = clone $this->myFunc; or is this a heresy? ;) -- 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] First-class callable syntax
On Thu, May 20, 2021, 15:48 Nikita Popov wrote: > Hi internals, > > I'd like to present an RFC for a first-class callable syntax, which is > intended as a simpler alternative to the partial function application (PFA) > proposal: > > https://wiki.php.net/rfc/first_class_callable_syntax > > This looks good to me. Following some discussions, I understand how this syntax was proposed. Maybe update also the PFA RFC to be up to date with the latest info. Also, considering the resolution between property and method (or between constants and static methods) , it's clear that we need a syntax that looks like the usual invocation:(...), or (?), (...?), ($), ($$), (...$) etc. I have few question related to some behavior. If we have closure: $c = function() {}; Will this be true? $c(...) === $c; similarly with how it happens for Closure::fromCallable($c) === $c; since the RFC makes a lot of comparisons between them. Also, from performance point of view, do we expect the new syntax to behave similarly? And another not practical one: Will this be valid? $c(...)(...)(...)(...)(); Regards, Alex
Re: [PHP-DEV] [RFC] First-class callable syntax
Ondřej Mirtes wrote: > Hi, I’m confused by the syntax, when I read it,... That is true of almost any new syntax. Some of us are still getting used to seeing \ in front of function calls. It doesn't mean that the syntax choice is a bad one, just that it's something new. For me, the main questions I ask myself is: * could I explain what the syntax means to a junior developer? * would the junior developer be able to remember it? For both of those questions, the suggested syntax seems fine. Guilliam Xavier wrote: > I wouldn't want to start new bikeshedding here; is there a > place that would be more appropriate to gather the possibilities Unfortunately I don't think we do have a better place. Being able to fork conversations so that "not completely off-topic, but not really that productive and getting in the way of the main discussion stuff", can be out of the way, but findable, is a feature that would be really useful for discussions. Nikita Popov wrote: > I am generally open to using a different syntax, Well, you might regret saying that. It might not be appropriate*, but having $() as marking a closure/callable seems 'clean' to me. Though obviously novel and likely to cause gut reactions: $(strlen); $($foo, bar); $(Foo, quux); $($foo->fn); $($foo); $($fn); Each would be equivalent to: Closure::fromCallable('strlen'); Closure::fromCallable([$foo, 'bar']); Closure::fromCallable([Foo::class, 'quux']); // aka Closure::fromCallable('Foo::quux'); Closure::fromCallable($foo->fn); Closure::fromCallable($foo); Closure::fromCallable($fn); For the code: class Foo { public $fn; public function __construct($fn) { $this->fn = $fn; } public function bar() { } public static function quux() { } public function __invoke() { } } $fn = function() { echo "I am function\n"; }; $foo = new Foo($fn); cheers Dan Ack * at least in the sense that it's a big syntax change, for a relatively small feature. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, 20 May 2021 at 10:17, Ondřej Mirtes wrote: > Hi, I’m confused by the syntax, when I read it, I think to myself “I know > this, this is just a method call… oh wait, it’s actually a callable”. It > really makes my head hurt. > I agree with the first point — slightly confusing initially, but I can see myself get used to it. I suggested strlen(=>) as an alternative, not sure if that would cause parsing difficulties. > Also, static analysers already have to reason about current code, so > PHPStan (and Psalm probably too) already supports referencing to callables > as strings (global functions) and arrays (methods): > > $name = 'date'; > $name(1); // Parameter #1 $format of callable 'date' expects string, int > given. > I disagree here: while Psalm can understand what's going on in the above code, callables are definitely (in my opinion) the most hairy part of PHP's type system. The RFC gives an example of something not caught by either PHPStan or Psalm: https://psalm.dev/r/21217fdfb4 https://phpstan.org/r/6cc8cdc2-ecc6-403d-887d-0f12f4813b75 Discouraging this particular formulation would make affected codebases safe from this particular error. Best wishes, Matt
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, May 20, 2021 at 6:22 PM Luis Henrique wrote: > On 20/05/2021 12:55, Guilliam Xavier wrote: > > On Thu, May 20, 2021 at 5:12 PM Nikita Popov > wrote: > > > >> On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes wrote: > >> > >>> Hi, I’m confused by the syntax, when I read it, I think to myself “I > know > >>> this, this is just a method call… oh wait, it’s actually a callable”. > It > >>> really makes my head hurt. > >>> > >> > >> Yes, I can see how that could be confusing. The current syntax is > chosen to > >> be a subset of the partial function application proposal. However, I > would > >> also be happy with some other syntax that makes it clearer that this is > >> acquiring a callable and not performing a call. > >> > > > > Hi, several other syntaxes have been proposed to consideration in the PFA > > thread, and I wouldn't want to start new bikeshedding here; is there a > > place that would be more appropriate to gather the possibilities (like a > > kind of updatable list)? > > > > Thanks, > > > > I am much more into advanced features of PFA, but is this case why not > just use the & operator for "function reference" like C/C++? It is well > known and solves the ambiguity with method/function call. > > $fn = &$this->myFunc; > > $fn = > > $fn = ::myFunc; > Unfortunately, this syntax is trivially ambiguous. "$fn = &$this->myFunc" is currently already interpreted as a reference assignment of the property $this->myFunc. I have updated https://wiki.php.net/rfc/first_class_callable_syntax#syntax_choice to discuss some of the obvious suggestions and why they don't work. Regards, Nikita
Re: [PHP-DEV] [RFC] First-class callable syntax
On 20/05/2021 12:55, Guilliam Xavier wrote: On Thu, May 20, 2021 at 5:12 PM Nikita Popov wrote: On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes wrote: Hi, I’m confused by the syntax, when I read it, I think to myself “I know this, this is just a method call… oh wait, it’s actually a callable”. It really makes my head hurt. Yes, I can see how that could be confusing. The current syntax is chosen to be a subset of the partial function application proposal. However, I would also be happy with some other syntax that makes it clearer that this is acquiring a callable and not performing a call. Hi, several other syntaxes have been proposed to consideration in the PFA thread, and I wouldn't want to start new bikeshedding here; is there a place that would be more appropriate to gather the possibilities (like a kind of updatable list)? Thanks, I am much more into advanced features of PFA, but is this case why not just use the & operator for "function reference" like C/C++? It is well known and solves the ambiguity with method/function call. $fn = &$this->myFunc; $fn = $fn = ::myFunc; -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, May 20, 2021 at 5:12 PM Nikita Popov wrote: > On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes wrote: > > > Hi, I’m confused by the syntax, when I read it, I think to myself “I know > > this, this is just a method call… oh wait, it’s actually a callable”. It > > really makes my head hurt. > > > > Yes, I can see how that could be confusing. The current syntax is chosen to > be a subset of the partial function application proposal. However, I would > also be happy with some other syntax that makes it clearer that this is > acquiring a callable and not performing a call. > Hi, several other syntaxes have been proposed to consideration in the PFA thread, and I wouldn't want to start new bikeshedding here; is there a place that would be more appropriate to gather the possibilities (like a kind of updatable list)? Thanks, -- Guilliam Xavier
Re: [PHP-DEV] [RFC] First-class callable syntax
On Thu, May 20, 2021 at 4:16 PM Ondřej Mirtes wrote: > Hi, I’m confused by the syntax, when I read it, I think to myself “I know > this, this is just a method call… oh wait, it’s actually a callable”. It > really makes my head hurt. > Yes, I can see how that could be confusing. The current syntax is chosen to be a subset of the partial function application proposal. However, I would also be happy with some other syntax that makes it clearer that this is acquiring a callable and not performing a call. > Also, static analysers already have to reason about current code, so > PHPStan (and Psalm probably too) already supports referencing to callables > as strings (global functions) and arrays (methods): > > $name = 'date'; > $name(1); // Parameter #1 $format of callable 'date' expects string, int > given. > This is only possible for static analyzers that implement relatively sophisticated and non-local analysis. If you see a random ['A', 'b'] in the code, you cannot know whether this is just an array, or supposed to be a reference to A::b() without following the data-flow of the value, and checking whether it is going to be used in a position that requires a callable, or passed to a callable parameter. This means that answering a simple question like "find all references to this method in the code base" also requires solving data-flow and type inference problems, which is ... not great. Apart from the static analysis perspective, having a first-class callable syntax also fixes the current scoping problems that plague callables (which can be worked around using Closure::fromCallable), and allows the elimination of the callable type in favor of the Closure type, which has much more well-defined semantics. (You will note that callable is already not usable for typed properties.) I don't think there's any question that we need a first-class callable syntax ... it's more a question of how exactly it should look like. The PFA RFC proposed one possibility. This RFC is another, more limited form. Some people have also suggested that we should "just" allow writing strlen to reference the strlen function etc, but this is not viable without a prior unification of all symbol tables (such syntax conflicts with constant access, class constant access and object property access). Regards, Nikita
Re: [PHP-DEV] [RFC] First-class callable syntax
Hi, I’m confused by the syntax, when I read it, I think to myself “I know this, this is just a method call… oh wait, it’s actually a callable”. It really makes my head hurt. Also, static analysers already have to reason about current code, so PHPStan (and Psalm probably too) already supports referencing to callables as strings (global functions) and arrays (methods): $name = 'date'; $name(1); // Parameter #1 $format of callable 'date' expects string, int given.