[RFC] [DISCUSSION]: Closure from callable

php.internals

Dan Ackroyd

10 years ago
Hello internals, I'd like to start a discussion of a proposal to allow closures to be created in user-land without having to use a whole lot of reflection code. https://wiki.php.net/rfc/closurefromcallable Thanks to Joe and Bob for the assistance in the patch. cheers Dan Ack

Michael Wallner

10 years ago
On 29/09/15 16:23, Dan Ackroyd wrote:
> Hello internals, > > I'd like to start a discussion of a proposal to allow closures to be > created in user-land without having to use a whole lot of reflection > code. > > https://wiki.php.net/rfc/closurefromcallable > > Thanks to Joe and Bob for the assistance in the patch.
+1, I'm not sure it belongs into ext/reflection, though?
-- Regards, Mike

Joe Watkins

10 years ago
+1 on the concept, I tried to argue it belongs in /Zend too ... still think it does ... On Tue, Sep 29, 2015 at 3:50 PM, Michael Wallner <mike@php.net> wrote:

Dan Ackroyd

10 years ago
> On Tue, Sep 29, 2015 at 3:50 PM, Michael Wallner <mike@php.net> wrote: >> +1, I'm not sure it belongs into ext/reflection, though?
On 29 September 2015 at 14:54, Joe Watkins <pthreads@pthreads.org> wrote:
> +1 on the concept, I tried to argue it belongs in /Zend too ... still think
Sure I'll move it to there when I finish off the patch with the slight improvements in the error messages that are needed. cheers Dan

Ryan Pallas

10 years ago
On Tue, Sep 29, 2015 at 8:23 AM, Dan Ackroyd <danack@basereality.com> wrote:

Levi Morrison

10 years ago
On Tue, Sep 29, 2015 at 8:23 AM, Dan Ackroyd <danack@basereality.com> wrote:
> Hello internals, > > I'd like to start a discussion of a proposal to allow closures to be > created in user-land without having to use a whole lot of reflection > code. > > https://wiki.php.net/rfc/closurefromcallable > > Thanks to Joe and Bob for the assistance in the patch. > > cheers > Dan > Ack > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php
I fixed a formatting error in https://wiki.php.net/rfc/closurefromcallable#other_languages Overall it looks good. I have a small quibble with:
> However this RFC takes the position that it is inappropriate to have a separate function per type.
But that wouldn't make me vote no. I think it's valuable to have both (just not with Hack's names… those are awful).

Andrew Faulds

10 years ago
Hi Dan, Dan Ackroyd wrote:
> I'd like to start a discussion of a proposal to allow closures to be > created in user-land without having to use a whole lot of reflection > code. > > https://wiki.php.net/rfc/closurefromcallable > > Thanks to Joe and Bob for the assistance in the patch.
I've wanted something like this for a long time. All that being said, though, I'm not sure a function is the way to do it. In C I can just use the name of a function to get a function pointer, couldn't we do this in PHP somehow? That is, `array_map(strlen, ["foo", "foobar", "elePHPant"]);` rather than `array_map(closure('strlen'), ...)`. I realise this would be a bit difficult to do, but I think it's a nicer approach. Thanks.
-- Andrea Faulds http://ajf.me/

Bishop Bettini

10 years ago
On Tue, Sep 29, 2015 at 10:23 AM, Dan Ackroyd <danack@basereality.com> wrote:
> Hello internals, > > I'd like to start a discussion of a proposal to allow closures to be > created in user-land without having to use a whole lot of reflection > code. > > https://wiki.php.net/rfc/closurefromcallable > > Thanks to Joe and Bob for the assistance in the patch. >
Yes, but with one question: should it be named "reflection_closure"? (I don't like that, but:) the user-land rules [1] say it will begin with the extension name, and the coding standards [2] say it will if it's part of a parent set. Maybe put it in core instead of reflection extension. Or, since ext/reflection doesn't have any functions, we can bypass it. [1]: http://php.net/manual/en/userlandnaming.rules.php [2]: https://github.com/php/php-src/blob/master/CODING_STANDARDS#L110

Andrew Faulds

10 years ago
Hi Dan, Dan Ackroyd wrote:
> > https://wiki.php.net/rfc/closurefromcallable >
Here's some further thoughts on this RFC. I don't like that it's a function. Closure is a class, classes can have static and instance methods, and closure() looks like something that should be a method on Closure. In fact, it creates a closure, which a constructor would usually do. If we want to make it avoid duplication, okay, then it could be a static method. But I'm unconvinced it should be a function. Constructors go in their classes, we don't leave them lying around as global functions. That's messy and inconsistent. You say that it being a function would mean we could make it a language construct to improve performance. But making it a language construct means making Closure a reserved word, and probably break things, right? We don't need to use a language construct for performance, though. Zend Engine III lets us inline certain commonly-used functions (strlen and is_int, for example). This requires that function references be absolute with no ambiguity, which means prefixed with a backslash or being used in non-namespaced code... but in practice few people type \ before root namespace functions. Thing is, this ambiguity doesn't exist for classes. Any usage of, say, \Closure::from (if we were to call it that) would be inlineable, unless the programmer uses variable variables. So, I don't buy that we should use a function for potential future performance with a language construct, since we can do inlining with no breakage *right now* if we use a static method. If we *were* to make it a language construct, we should do it properly and use proper syntax. No closure("foo") or closure(array("SomeClass", "methodName")), instead closure(foo) or closure(SomeClass::methodName). And in that case, we'd probably use the existing reserved word for this: callable. By the way, as proposed, closure() has a performance penalty. You're calling a function to convert a callable into a closure, but that's almost always unnecessary. You could simply use the callable directly. closure(), as proposed, could also be done quite simply in userland, and even optimised. Why should it be added to PHP core? So, I think this proposal needs considerable work. Thanks.
-- Andrea Faulds http://ajf.me/

Dan Ackroyd

10 years ago
On 4 October 2015 at 09:54, Andrea Faulds <ajf@ajf.me> wrote:
> If we want to make it avoid duplication, > okay, then it could be a static method.
Okay, I'll update the RFC to be `Closure::fromCallable()` rather than a function. I was hoping to have something less verbose than that, but you're probably right when you say we ought to do that properly with it's own syntax rather than just a function. Oh using a static method will rule out being able to alias the function with the use function syntax. use function closure as _; //can work use function Closure::fromCallable as _; //doesn't work Which is slightly annoying but ¯\_(ツ)_/¯ Additionally, I linked to the wrong branch in the RFC. It should be: https://github.com/php/php-src/compare/master...Danack:closureSimpler cheers Dan

Stas Malyshev

10 years ago
Hi!
> I don't like that it's a function. Closure is a class, classes can have > static and instance methods, and closure() looks like something that > should be a method on Closure. In fact, it creates a closure, which a > constructor would usually do. If we want to make it avoid duplication, > okay, then it could be a static method. But I'm unconvinced it should be > a function. Constructors go in their classes, we don't leave them lying > around as global functions. That's messy and inconsistent.
Agree. Closure::fromCallable sounds like a good option. That being said, I'm not sure how much need is for this - i.e. you can always make a closure by just doing function() { return $this->blah(); } if you need it. But the bigger question would be why you need more specific type (Closure) instead of more generic (callable) and aren't you overspecifying in that case? And if we're into performance optimization - which should almost never be done on language construct level, because performance considerations vary and language constructs are forever - then it would be much better to look into tail call optimizations. That would solve the barrier between [$this, "foo"] and function() { return $this->foo(); } in many practical cases.
-- Stas Malyshev smalyshev@gmail.com

Mike

10 years ago
On 4 Oct 2015 21:50, "Stanislav Malyshev" <smalyshev@gmail.com> wrote:
> > Hi! > > > I don't like that it's a function. Closure is a class, classes can have > > static and instance methods, and closure() looks like something that > > should be a method on Closure. In fact, it creates a closure, which a > > constructor would usually do. If we want to make it avoid duplication, > > okay, then it could be a static method. But I'm unconvinced it should be > > a function. Constructors go in their classes, we don't leave them lying > > around as global functions. That's messy and inconsistent. > > Agree. Closure::fromCallable sounds like a good option. > That being said, I'm not sure how much need is for this - i.e. you can > always make a closure by just doing function() { return $this->blah(); } > if you need it. But the bigger question would be why you need more > specific type (Closure) instead of more generic (callable) and aren't > you overspecifying in that case? > > And if we're into performance optimization - which should almost never > be done on language construct level, because performance considerations > vary and language constructs are forever - then it would be much better > to look into tail call optimizations. That would solve the barrier > between [$this, "foo"] and function() { return $this->foo(); } in many > practical cases. >
I think the origin of this intention is, that calling a callable just by `$callable()` does not work consistently -- see the othe RFC by Dan -- and that call_user_func() suffers from a (mythical?) performance issue.

Stas Malyshev

10 years ago
Hi!
> I think the origin of this intention is, that calling a callable just > by `$callable()` does not work consistently -- see the other RFC by > Dan --
Maybe we should fix $callable() then? :) I see no reason why $() shouldn't work on all callables. That's why they are "callable" after all.
> and that call_user_func() suffers from a (mythical?) performance > issue.
Fixing performance issues in a specific functions by introducing a language-level constructs does not sound like the best option to me. I would first ensure the issue exists and then look into how it can be fixed with this specific function.
-- Stas Malyshev smalyshev@gmail.com

Dan Ackroyd

10 years ago
On 4 October 2015 at 23:02, Stanislav Malyshev <smalyshev@gmail.com> wrote:
> Hi! > > That being said, I'm not sure how much need is for this - i.e. you can > always make a closure by just doing function() { return $this->blah(); } > if you need it.
As I said before (and as the RFC says), there are ways to work around not having it. But having to duplicate code and keep it in sync is a pain and indicates that something is missing from the language.
> And if we're into performance optimization
We're not. The performance stuff is nice side benefit, but it's not the main aim of the RFC which is to make life easier for people who want to create closures in PHP.
> But the bigger question would be why you need more > specific type (Closure) instead of more generic (callable)
IMO it's actually the other way round; closures are more generic than callables. To get the parameters of a closure via inspection you only ever need to use ReflectionFunction. To get the parameters of a callable, you need to figure out first what type of callable it is, and then either use ReflectionFunction or ReflectionClass + ReflectionMethod as appropriate. And also callables are just a bit insane currently:
> Maybe we should fix $callable() then? :)
Maybe we should! I announced a draft RFC a week ago: http://news.php.net/php.internals/88545 Only one person (off list) has given any feedback on it so far. I invite more feedback! Even if it's just "that's an amazing RFC Dan, you've covered every possible aspect of the changes that need to be discussed." that would still be good feedback to get. But as that RFC could only be introduced for PHP 8 at the earliest, and callable still won't be as easy to work with as closures, the feedback I have received is that people would very much appreciate having this RFC for 7.1 cheers Dan

Ben Scholzen 'DASPRiD'

10 years ago
On 29.09.2015 16:23, Dan Ackroyd wrote:
> Hello internals, > > I'd like to start a discussion of a proposal to allow closures to be > created in user-land without having to use a whole lot of reflection > code. > > https://wiki.php.net/rfc/closurefromcallable > > Thanks to Joe and Bob for the assistance in the patch.
Your first example on the page is a little weird to me. You claim that one currently needs to have the callback method public, although you can easily work around that. Instead of doing this, as in your example: return [$this, 'genericValidation'] you could just do this: return function ($data) { return $this->genericValidation($data); } and keep the actual callback method private/protected.
-- Ben Scholzen 'DASPRiD' Community Review Team Member | mail@dasprids.de Zend Framework | http://www.dasprids.de

Dan Ackroyd

10 years ago
On 4 October 2015 at 10:16, Ben Scholzen 'DASPRiD' <mail@dasprids.de> wrote:
> > you could just do this: > > return function ($data) { return $this->genericValidation($data); } >
> you could just do this: > > return function ($data) { return $this->genericValidation($data); }
Yes, there are workarounds possible that don't need anything new i.e. either implementing the closure generation function in userland or doing a programmer generated closure. The downside to writing the closure by hand is more obvious when the parameters and return value is typed: class Validator { private function genericValidation(callable $foo, string bar, UserData $userData) : string {...} public function getValidatorCallback($validationType) { //... return function (callable $foo, string bar, UserData $userData) : string { return $this->genericValidation($data); }; } } All of the type checking has to be repeated in the programmer generated closure, which is annoying as it means you have to keep code in different places synchronized. Additionally PHP is kindof slow in function calling. Wrapping the method to be called in a userland land function makes calling it be much slower than using an internally generated closure. In addition to the function call overhead, all of the paramters and return type will have their type checked twice; once when they're pased to the closure and once when they're passed to the method. cheers Dan

Marco Pivetta

10 years ago
As mentioned already by Andrea: why not just a named constructor on the closure class, like `Closure::fromCallable(callable $callable)`? Much simpler, easier to find, etc etc... On Sep 29, 2015 11:23, "Dan Ackroyd" <danack@basereality.com> wrote:

Pavel Kouřil

10 years ago
On Sun, Oct 4, 2015 at 12:41 PM, Marco Pivetta <ocramius@gmail.com> wrote:
> As mentioned already by Andrea: why not just a named constructor on the > closure class, like `Closure::fromCallable(callable $callable)`? > Much simpler, easier to find, etc etc... > On Sep 29, 2015 11:23, "Dan Ackroyd" <danack@basereality.com> wrote: > >> Hello internals, >> >> I'd like to start a discussion of a proposal to allow closures to be >> created in user-land without having to use a whole lot of reflection >> code. >> >> https://wiki.php.net/rfc/closurefromcallable >> >> Thanks to Joe and Bob for the assistance in the patch. >> >> cheers >> Dan >> Ack >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php >> >>
Hello, and what about changing the empty ctor of Closure to __construct(callable $callable)? And the usage would be new Closure([$this, 'abc']);? Regards Pavel Kouřil

Dan Ackroyd

10 years ago
Hi Pavel, On 4 October 2015 at 11:31, Pavel Kouřil <pajousek@gmail.com> wrote:
> > and what about changing the empty ctor of Closure to > __construct(callable $callable)? > > And the usage would be new Closure([$this, 'abc']);? >
I'm going to change the RFC to use the 'named constructor' Closure::fromCallable which should address the naming concerns. IMO, default constructors are bad and I encourage people to stop using them. In this case they are bad as they can't be passed as a callable. Either 'closure' or 'Closure::fromCallable can be passed as a callable: function createClosure($callable, $name) { // do stuff return $callable($name); } foo('closure', 'bar'); foo([Closure::class, 'fromCallable'], 'bar'); foo('librarySpecificClosureGenerator', 'bar'); In addition, it's inappropriate to use default constructors in a class where it is even slightly likely that you are going to have more than one way of constructing that object, as you then have to hack around the fact that you want to create other named constructors that don't call the default constructor. cheers Dan