Re: [PHP-DEV] [RFC]

This is only part of a thread. view whole thread
  108484
February 11, 2020 14:51 chasepeeler@gmail.com (Chase Peeler)
On Tue, Feb 11, 2020 at 8:19 AM Nikita Popov ppv@gmail.com> wrote:

> On Tue, Feb 11, 2020 at 1:43 PM Manuel Canga <manuelcanga@gmail.com> > wrote: > > > On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas > > grekas+php@gmail.com> wrote: > > > > > > > > > > > > Le mar. 11 févr. 2020 à 12:52, Diogo Galvao <diogo86@gmail.com> a > écrit > > : > > >> > > >> On Tue, Feb 11, 2020 at 8:14 AM Manuel Canga <manuelcanga@gmail.com> > > wrote: > > >> > > > >> > Hi internals, > > >> > I Would like to present a possible new "::func resolution" for your > > >> > consideration. > > >> ... > > >> > use function \My\I18N\i18n_translate; > > >> > > > >> > $mapped_array = array_map(i18n_translate::func, $array); > > >> ... > > >> > What is your opinion ? Do you see it useful ? > > >> > > >> I've wished for this on many occasions and think it'd be really > useful, > > as long > > >> as it could work with methods as well: > > >> > > >> $mapped_array = array_map(I18N::translate::function, $array); > > >> > > >> For what it's worth I guess it could just return [I18N::class, > > 'translate']. > > > > > > > > > I wish this would return a Closure instead, making $foo::function the > > equivalent of Closure::fromCallable($foo). > > > > Hi, Nicolas, > > > > Currently, when ::class is used, class with > > can or cannot exists in that moment. > > Using ::func( or ::function ), I think > > should keep the same behavior. > > > > Using ::func as alias of "Closure::fromCallable" check if function > > exists in that moment. It is certainly useful, but, I think it's more > > important be consistent > > > > Checking whether the function exists is really unavoidable for functions, > because you don't know whether > > namespace Foo; > var_dump(strlen::function); > > refers to 'Foo\strlen' or 'strlen', without first trying to look up the > former. > > I agree with Nicolas that this kind of feature would provide the most value > if it created a Closure. This would circumvent all the issues outlined in > https://wiki.php.net/rfc/consistent_callables. > > Regards, > Nikita >
Can anyone thing of a use-case where you would want a string name of a function and a callable would not be acceptable, besides possibly debugging code that said 'echo "I'm calling ".myfunction::function;'? Everything that I can think of that accepts a function name, also accepts a callable (e.g. array_map), but I could be forgetting something. If not, then I think it makes sense to return a callable. It might not be entirely consistent with the behavior of ::class, but, a class isn't entirely consistent with a method/function either, so I think there is some latitude for small differences. As for the ::func vs ::function. I think ::function is safer, since it's a reserved word. Otherwise you might run into issues with something like this: class foo { const func = "bar"; } function foo(){} echo foo::func; Probably not something that happens very often, but, I think the 4 extra characters to prevent it would be worth it. -- Chase Peeler chasepeeler@gmail.com
  108485
February 11, 2020 15:13 manuelcanga@gmail.com (Manuel Canga)
On Tue, 11 Feb 2020 at 15:51, Chase Peeler <chasepeeler@gmail.com> wrote:
> > > > On Tue, Feb 11, 2020 at 8:19 AM Nikita Popov ppv@gmail.com> wrote: >> >> On Tue, Feb 11, 2020 at 1:43 PM Manuel Canga <manuelcanga@gmail.com> wrote: >> >> > On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas >> > grekas+php@gmail.com> wrote: >> > > >> > > >> > > >> > > Le mar. 11 févr. 2020 à 12:52, Diogo Galvao <diogo86@gmail..com> a écrit >> > : >> > >> >> > >> On Tue, Feb 11, 2020 at 8:14 AM Manuel Canga <manuelcanga@gmail.com> >> > wrote: >> > >> > >> > >> > Hi internals, >> > >> > I Would like to present a possible new "::func resolution" for your >> > >> > consideration. >> > >> ... >> > >> > use function \My\I18N\i18n_translate; >> > >> > >> > >> > $mapped_array = array_map(i18n_translate::func, $array); >> > >> ... >> > >> > What is your opinion ? Do you see it useful ? >> > >> >> > >> I've wished for this on many occasions and think it'd be really useful, >> > as long >> > >> as it could work with methods as well: >> > >> >> > >> $mapped_array = array_map(I18N::translate::function, $array); >> > >> >> > >> For what it's worth I guess it could just return [I18N::class, >> > 'translate']. >> > > >> > > >> > > I wish this would return a Closure instead, making $foo::function the >> > equivalent of Closure::fromCallable($foo). >> > >> > Hi, Nicolas, >> > >> > Currently, when ::class is used, class with >> > can or cannot exists in that moment. >> > Using ::func( or ::function ), I think >> > should keep the same behavior. >> > >> > Using ::func as alias of "Closure::fromCallable" check if function >> > exists in that moment. It is certainly useful, but, I think it's more >> > important be consistent >> > >> >> Checking whether the function exists is really unavoidable for functions, >> because you don't know whether >> >> namespace Foo; >> var_dump(strlen::function); >> >> refers to 'Foo\strlen' or 'strlen', without first trying to look up the >> former. >> >> I agree with Nicolas that this kind of feature would provide the most value >> if it created a Closure. This would circumvent all the issues outlined in >> https://wiki.php.net/rfc/consistent_callables. >> >> Regards, >> Nikita > > > Can anyone thing of a use-case where you would want a string name of a function and a callable would not be acceptable, besides possibly debugging code that said 'echo "I'm calling ".myfunction::function;'? Everything that I can think of that accepts a function name, also accepts a callable (e.g. array_map), but I could be forgetting something. > > If not, then I think it makes sense to return a callable. It might not be entirely consistent with the behavior of ::class, but, a class isn't entirely consistent with a method/function either, so I think there is some latitude for small differences. > > As for the ::func vs ::function. I think ::function is safer, since it's a reserved word. Otherwise you might run into issues with something like this: > > class foo { > const func = "bar"; > } > > function foo(){} > > echo foo::func; > > Probably not something that happens very often, but, I think the 4 extra characters to prevent it would be worth it. > > -- > Chase Peeler > chasepeeler@gmail.com
Hi, Chase, Yes, '::function' is better. I don't know because I thought 'func' was used with short functions. One case which string can be useful but a callable would not be acceptable is: array_map([I18N::class, translate::function] ); Although it's true that can have side effects. Thanks, Chase,
  108519
February 12, 2020 21:36 rowan.collins@gmail.com (Rowan Tommins)
On 11/02/2020 15:13, Manuel Canga wrote:
> One case which string can be useful but a callable would not be acceptable is: > > array_map([I18N::class, translate::function] );
I wouldn't expect that to work anyway, because the whole purpose of the keyword would be to resolve "translate" as a function name, not a method name, e.g. use Acme\Global\I18N; use function Acme\Global\translate; var_dump([I18N::class, translate::function]); # array(0 => 'Acme\Global\I18N', 1 => 'Acme\Global\translate') # not a valid callable var_dump([I18N::class, 'translate']); # array(0 => 'Acme\Global\I18N', 1 => 'translate'); # this was what was intended If you didn't want to quote the method, you'd need some other syntax that took both class and method name, like: [I18N, translate]::callable Or as proposed elsewhere in the thread: $(I18N::translate) Regards, -- Rowan Tommins (né Collins) [IMSoP]
  108521
February 12, 2020 21:47 manuelcanga@gmail.com (Manuel Canga)
El mié., 12 feb. 2020 22:36, Rowan Tommins collins@gmail.com>
escribió:

> On 11/02/2020 15:13, Manuel Canga wrote: > > One case which string can be useful but a callable would not be > acceptable is: > > > > array_map([I18N::class, translate::function] ); > > > I wouldn't expect that to work anyway, because the whole purpose of the > keyword would be to resolve "translate" as a function name, not a method > name, e.g. > > use Acme\Global\I18N; > use function Acme\Global\translate; > > var_dump([I18N::class, translate::function]); > > # array(0 => 'Acme\Global\I18N', 1 => 'Acme\Global\translate') > # not a valid callable > > var_dump([I18N::class, 'translate']); > > # array(0 => 'Acme\Global\I18N', 1 => 'translate'); > # this was what was intended > > > If you didn't want to quote the method, you'd need some other syntax > that took both class and method name, like: > > [I18N, translate]::callable > > Or as proposed elsewhere in the thread: > > $(I18N::translate) > > > Regards, >
Hi, You is importing function and you are using different. It is the same case like: namespace MyProject; use Vendor/Controller; class Controller extends Controller { }
>
  108522
February 12, 2020 22:01 rowan.collins@gmail.com (Rowan Tommins)
On 12/02/2020 21:47, Manuel Canga wrote:
> You is importing function and you are  using different. It is the same > case like: > > namespace MyProject; > > use Vendor/Controller; > > class Controller extends Controller { > }
In that example, you're defining two things of the same type with the same name, which would be an error; that's not what was happening in my example. In a call like Acme\Global\I18N::translate, or a callable like ['Acme\Global\I18N', 'translate'], the "translate" part never refers to a function in the current namespace, or indeed any namespace, only to a method of that particular class. But if you write ['Acme\Global\I18N', translate::function], there's no way for the engine to know that you wanted a method name rather than a function name, so it will try to resolve it in the current namespace and import list. That will either give an error, because there is no function called translate; or it will give you a fully-qualified name, which isn't what you wanted, you just wanted the string 'translate'. Regards, -- Rowan Tommins (né Collins) [IMSoP]
  108526
February 12, 2020 23:12 manuelcanga@gmail.com (Manuel Canga)
El mié., 12 feb. 2020 23:01, Rowan Tommins collins@gmail.com>
escribió:

> On 12/02/2020 21:47, Manuel Canga wrote: > > You is importing function and you are using different. It is the same > > case like: > > > > namespace MyProject; > > > > use Vendor/Controller; > > > > class Controller extends Controller { > > } > > > In that example, you're defining two things of the same type with the > same name, which would be an error; that's not what was happening in my > example. > > In a call like Acme\Global\I18N::translate, or a callable like > ['Acme\Global\I18N', 'translate'], the "translate" part never refers to > a function in the current namespace, or indeed any namespace, only to a > method of that particular class. > > But if you write ['Acme\Global\I18N', translate::function], there's no > way for the engine to know that you wanted a method name rather than a > function name, so it will try to resolve it in the current namespace and > import list. That will either give an error, because there is no > function called translate; or it will give you a fully-qualified name, > which isn't what you wanted, you just wanted the string 'translate' >
You're right about my example. This is good example: namespace MyProject; use Vendor\Controller; class MyController extends Controller { ...... } This is an error, if Controller has actually MyProyect namespace. Then, you have two options: 1. Change import 2. Add namespace to class Controller In your example, you has the same options:
> 1. Change import
2. Add namespace: ['Acme\Global\I18N',\translate::function] Explain: When you do: [ class, method ] or [ $object, method ]. Method has not namespace, you write it without namespace( like global functions ) then you do the same with ::function. This is [ class, \method::function ] or [ $object, \method::function ] Other example: $class = \MyClass::class; $method = \method::function; $obj = new $class(); $obj->$method(); and... $class = '\MyClass'; $method = 'method'; $obj = new $class(); $obj->$method(); Both are the same, but first is more semantic.
  108531
February 13, 2020 07:57 rowan.collins@gmail.com (Rowan Tommins)
On 12 February 2020 23:12:34 GMT+00:00, Manuel Canga <manuelcanga@gmail.com> wrote:
>El mié., 12 feb. 2020 23:01, Rowan Tommins collins@gmail.com> >escribió: >In your example, you has the same options: > >> >1. Change import >2. Add namespace: > >['Acme\Global\I18N',\translate::function]
There is no collision between 'Foo::translate()' and 'translate()', so there is no reason to change the import. That's true of executing the functions, so it should remain be true of resolving them to strings.
>Explain: > >When you do: [ class, method ] or [ $object, method ]. Method has not >namespace, you write it without namespace( like global functions )
I think this is where we are thinking differently. A method name is not "like a global function", it's just a name; it doesn't belong in the same category. You might have any number of classes that use the same method name, but with completely different parameters and purpose, so "a method named foo" isn't a useful concept outside some specific class or interface. On the other hand, you can only have one function with a particular fully-qualified name, and the proposed feature is a way of referencing that.
>Other example: > >$class = \MyClass::class; >$method = \method::function; > >and... > >$class = '\MyClass'; >$method = 'method'; > >$obj = new $class(); >$obj->$method(); > >Both are the same, but first is more semantic.
This isn't semantic at all - it works only because \method::function happens to return the string you want, but so does \method::class; neither is actually labelling it as what it is, which is a method within class \MyClass. Importantly, it might not work at all, if ::function gives an error if the function doesn't exist. Regards, -- Rowan Tommins [IMSoP]
  108536
February 13, 2020 12:04 manuelcanga@gmail.com (Manuel Canga)
On Thu, 13 Feb 2020 at 08:58, Rowan Tommins collins@gmail.com> wrote:

> On 12 February 2020 23:12:34 GMT+00:00, Manuel Canga < > manuelcanga@gmail.com> wrote: > >El mié., 12 feb. 2020 23:01, Rowan Tommins collins@gmail.com> > >escribió: > >In your example, you has the same options: > > > >> > >1. Change import > >2. Add namespace: > > > >['Acme\Global\I18N',\translate::function] > > > There is no collision between 'Foo::translate()' and 'translate()', so > there is no reason to change the import. That's true of executing the > functions, so it should remain be true of resolving them to strings. > > There is collision with import which you added.
> > > >Explain: > > > >When you do: [ class, method ] or [ $object, method ]. Method has not > >namespace, you write it without namespace( like global functions ) > > > I think this is where we are thinking differently. A method name is not > "like a global function", it's just a name; it doesn't belong in the same > category. > > You might have any number of classes that use the same method name, but > with completely different parameters and purpose, so "a method named foo" > isn't a useful concept outside some specific class or interface. > > On the other hand, you can only have one function with a particular > fully-qualified name, and the proposed feature is a way of referencing that. > > Function as callable is different as regular function. Example:
http://sandbox.onlinephpfunctions.com/code/99408213d1ed740f60471646f16f9765d7efa93e namespace MyProject; function my_function() { //.... } my_function(); // it calls to \MyProject\my_function array_map('my_function', [] ); //* * Here, 'my_function' is only a string. Maybe a global function( without namespace ) or maybe a method or other case. With native array_map is a global function. However, you can have a function like this: function array_map( $method, $array) { \Collection::$method( $array ); } In both cases, you could do: array_map(\my_function::function, [] );
> > > >Other example: > > > >$class = \MyClass::class; > >$method = \method::function; > > > >and... > > > >$class = '\MyClass'; > >$method = 'method'; > > > >$obj = new $class(); > >$obj->$method(); > > > >Both are the same, but first is more semantic. > > > This isn't semantic at all - it works only because \method::function > happens to return the string you want, but so does \method::class; neither > is actually labelling it as what it is, which is a method within class > \MyClass. > > Importantly, it might not work at all, if ::function gives an error if > the function doesn't exist. > > ::function only would retrieve string, like as ::class, exists or not. In
fach, that code might not work at all due to class. What matter if Class does't exist ?. ::class doesn't produce error. Look: http://sandbox.onlinephpfunctions.com/code/0a8466a00974bc1ffc12b219569ced55753327bd If class doesn't exist, nothing happend. Thank, Rowan. You points of view are very interesting.
  108538
February 13, 2020 13:17 rowan.collins@gmail.com (Rowan Tommins)
On Thu, 13 Feb 2020 at 12:04, Manuel Canga <manuelcanga@gmail.com> wrote:

> > > On Thu, 13 Feb 2020 at 08:58, Rowan Tommins collins@gmail.com> > wrote: > >> On 12 February 2020 23:12:34 GMT+00:00, Manuel Canga < >> manuelcanga@gmail.com> wrote: >> >El mié., 12 feb. 2020 23:01, Rowan Tommins collins@gmail.com> >> >escribió: >> >In your example, you has the same options: >> > >> >> >> >1. Change import >> >2. Add namespace: >> > >> >['Acme\Global\I18N',\translate::function] >> >> >> There is no collision between 'Foo::translate()' and 'translate()', so >> there is no reason to change the import. That's true of executing the >> functions, so it should remain be true of resolving them to strings. >> >> There is collision with import which you added. >
There is no collision. This is perfectly valid PHP code: namespace Foo; use \explode; class Bomb { public static function explode() { return 'Bang! Bang!'; } } $bangs = explode(' ', Bomb::explode()); There is no ambiguity here, explode(...) refers precisely to the global function 'explode' which is unrelated to the class method 'Bomb::explode'. The same applies if I import the function from somewhere other than the global namespace: namespace Foo; use Acme\BetterArrayFunctions\explode; class Bomb { public static function explode() { return 'Bang! Bang!'; } } $bangs = explode(' ', Bomb::explode()); Again, there is no ambiguity, explode(...) refers precisely to the function 'Acme\BetterArrayFunctions\explode, which is unrelated to the class method 'Bomb::explode. "explode::function" should mean "if I run explode() here, what is the fully-qualified name of the function that would run?" If there's no ambiguity of which function would run, there's no ambiguity of what value ::function should return.
> * Here, 'my_function' is only a string. Maybe a global function( without > namespace ) or maybe a method or other case. With native array_map is a > global function. However, you can have a function like this: > > function array_map( $method, $array) { > \Collection::$method( $array ); > } > > In both cases, you could do: > > array_map(\my_function::function, [] ); >
In the second case, it would be incorrect to use \my_function::function, because you do not want the answer to the question "if I run \my_function() here, what is the fully-qualified name of the function that would run?" Annotating the argument that way wouldn't be useful for tools, either - e.g. an IDE like PHPStorm would see ::function and offer "jump to definition", but in this case it would jump to the definition of the global function my_function, which is actually irrelevant.
> > >> Importantly, it might not work at all, if ::function gives an error if >> the function doesn't exist. >> >> > ::function only would retrieve string, like as ::class, exists or not. In > fach, that code might not work at all due to class. What matter if Class > does't exist ?. ::class doesn't produce error. Look: > http://sandbox.onlinephpfunctions.com/code/0a8466a00974bc1ffc12b219569ced55753327bd >
As pointed out elsewhere, an implementation of ::function would need to be smarter than ::class, because unqualified function calls look first in the current namespace, then fall back to global scope. For example: namespace Foo; function explode() {} echo implode::function; // Should return 'implode', because that is what implode() would run echo explode::function; // Should return 'Foo\explode', because that is what explode() would run Regards, -- Rowan Tommins [IMSoP]
  108503
February 12, 2020 03:58 mike@newclarity.net (Mike Schinkel)
> On Feb 11, 2020, at 9:51 AM, Chase Peeler <chasepeeler@gmail.com> wrote: > > Can anyone thing of a use-case where you would want a string name of a > function and a callable would not be acceptable, besides possibly debugging > code that said 'echo "I'm calling ".myfunction::function;'? Everything that > I can think of that accepts a function name, also accepts a callable (e.g. > array_map), but I could be forgetting something. > > If not, then I think it makes sense to return a callable. It might not be > entirely consistent with the behavior of ::class, but, a class isn't > entirely consistent with a method/function either, so I think there is some > latitude for small differences. > > As for the ::func vs ::function. I think ::function is safer, since it's a > reserved word. Otherwise you might run into issues with something like this: > > class foo { > const func = "bar"; > } > > function foo(){} > > echo foo::func; > > Probably not something that happens very often, but, I think the 4 extra > characters to prevent it would be worth it.-------
Returning a _closure_ instead of a string would be providing a feature we _already_ have instead of one we do _not_ have. If we had ::function returning a string we could use Closure::fromCallable() to get a closure. Or today just use fn() => myfunc(). But if ::function instead returned a closure then there still would be no way to extract the name of a function as a string from a symbol where PHP can throw a warning if it does not recognize the symbol, such as in the case of a typo. Seems to me having a shorter syntax to get a closure is an orthogonal concern. If we want a shorthand for closure we should create an additional syntax for it but still provide a way to extract a function's name as a string from its symbol since that is currently _not_ possible. Getting a closure from a function symbol currently _is_ possible. Much better to provide ::function to return the name of the function and ::closure get a closure that can call the function. Or have ::function to return the name of the function and provide a syntax something like ${myfunc} to return a closure, which has been suggested later in this thread. Or best would be to add ::nameof for functions, method, classes, interfaces and traits and provide access to closures using either ::closure or a new short syntax. -Mike P.S. A language I specialized in during the late 80's and early 90's called Clipper implemented closures with the following syntax: - Without parameters: {|| expr } equivalent to function(){ return expr; } and fn() => expr; - With parameters: {|a,b| expr } equivalent to function(a,b){ return expr; } and fn(a,b) => expr; If we want a shorter syntax for function closures than fn() => myfunc() maybe we consider {|| myfunc() } which would be more general purpose than only returning a closure for a function?
  108505
February 12, 2020 07:12 guilliam.xavier@gmail.com (Guilliam Xavier)
On Wed, Feb 12, 2020 at 4:58 AM Mike Schinkel <mike@newclarity.net> wrote:
> > Or best would be to add ::nameof for functions, method, classes, interfaces and traits
One problem is how would `x::nameof` resolve when you have several `x` symbols (of distinct kinds) in scope? ``` namespace Theirs { class Foo {} const BAR = 2; function qux() { return '3'; } } namespace Mine { use Theirs\Foo as x; use const Theirs\BAR as x; use function Theirs\qux as x; var_dump(new x); // object(Theirs\Foo)#1 var_dump(x); // int(2) var_dump(x()); // string(1) "3" // *** hypothetical: *** assert(x::class === 'Theirs\Foo'); assert(x::const === 'Theirs\BAR'); assert(x::function === 'Theirs\qux'); assert(x::nameof === ???); } ```
> Returning a _closure_ instead of a string would be providing a feature we _already_ have instead of one we do _not_ have. > > If we had ::function returning a string we could use Closure::fromCallable() to get a closure. Or today just use fn() => myfunc(). > > But if ::function instead returned a closure then there still would be no way to extract the name of a function as a string from a symbol where PHP can throw a warning if it does not recognize the symbol, such as in the case of a typo. > > Seems to me having a shorter syntax to get a closure is an orthogonal concern. > > If we want a shorthand for closure we should create an additional syntax for it but still provide a way to extract a function's name as a string from its symbol since that is currently _not_ possible. Getting a closure from a function symbol currently _is_ possible. > > Much better to provide ::function to return the name of the function and ::closure get a closure that can call the function. > > Or have ::function to return the name of the function and provide a syntax something like ${myfunc} to return a closure, which has been suggested later in this thread.
That might deserve consideration indeed... -- Guilliam Xavier
  108506
February 12, 2020 08:37 mike@newclarity.net (Mike Schinkel)
> On Feb 12, 2020, at 2:12 AM, Guilliam Xavier xavier@gmail.com> wrote: > > On Wed, Feb 12, 2020 at 4:58 AM Mike Schinkel <mike@newclarity.net> wrote: >> >> Or best would be to add ::nameof for functions, method, classes, interfaces and traits > > One problem is how would `x::nameof` resolve when you have several `x` > symbols (of distinct kinds) in scope?
Excellent point. -Mike
  108525
February 12, 2020 22:47 rowan.collins@gmail.com (Rowan Tommins)
On 12/02/2020 03:58, Mike Schinkel wrote:
> Returning a_closure_ instead of a string would be providing a feature we_already_ have instead of one we do_not_ have.
Not really, because you still need a way to get the fully-qualified name of the function. This is not valid: namespace MyVendor\Something\Foo; function bar() {} \Closure::fromCallable('bar'); # Error: tries to resolve function '\bar' So if we had ::function which returned the name, and ::callable that returned a closure, the following would be equivalent: namespace MyVendor\Something\Foo; function bar() {} \Closure::fromCallable('MyVendor\Something\Foo\bar'); \Closure::fromCallable(bar::function); bar::callable; One combination would be ::function returning a string, and $() accepting one, which would give: $(bar::function) In practice, that would almost certainly be optimised into one instruction, so the main advantage would be consistency, unless there are use cases where a string rather than "any callable" is actually required.
> Or today just use fn() => myfunc().
That's not quite the same either: at the very least, you need to define it as fn(...$args) => myfunc(...$args) to pass through the parameters. But Closure::callable is able to copy the signature of the original function, including type information [1], so our hypothetical ::callable could do the same. [1] https://3v4l.org/XBOmJ Regards, -- Rowan Tommins (né Collins) [IMSoP]
  108528
February 13, 2020 03:33 mike@newclarity.net (Mike Schinkel)
> On Feb 12, 2020, at 5:47 PM, Rowan Tommins collins@gmail.com> wrote: > > On 12/02/2020 03:58, Mike Schinkel wrote: >> Returning a_closure_ instead of a string would be providing a feature we_already_ have instead of one we do_not_ have. > > > Not really, because you still need a way to get the fully-qualified name of the function. This is not valid: > > namespace MyVendor\Something\Foo; > function bar() {} > > \Closure::fromCallable('bar'); # Error: tries to resolve function '\bar' >
Why would ::function return a short name instead of the fully-qualified name? ::function should return the fully-qualified name as does ::class. Then the concern you state becomes a non-issue. If ::function returns a fully qualified name developers can always use string functions to get the short name if they need it.
> So if we had ::function which returned the name, and ::callable that returned a closure, the following would be equivalent: > > namespace MyVendor\Something\Foo; > function bar() {} > > \Closure::fromCallable('MyVendor\Something\Foo\bar'); > \Closure::fromCallable(bar::function); > bar::callable; > > One combination would be ::function returning a string, and $() accepting one, which would give: > > $(bar::function) > > In practice, that would almost certainly be optimised into one instruction, so the main advantage would be consistency, unless there are use cases where a string rather than "any callable" is actually required.
Yes.
>> Or today just use fn() => myfunc(). > > > That's not quite the same either: at the very least, you need to define it as fn(...$args) => myfunc(...$args) to pass through the parameters.
Assuming there are parameters.
> But Closure::callable is able to copy the signature of the original function, including type information [1], so our hypothetical ::callable could do the same.
True. ------- As an aside, I wish we had a new simpler form of metadata object that could provide the features of reflection quicker and easier and one that didn't need to be wrapped in a try{}catch{}. Something like this: - https://gist.github.com/mikeschinkel/78684d708358e1d101e319c7a2fdef9c But this being PHP — with its love of verbosity and complexity when adding classes, and disdain for adding something if a capability already exists, no matter how cumbersome it is — chances of this becoming a thing is pretty much moot. So I am just pontificating. -Mike
  108530
February 13, 2020 07:33 rowan.collins@gmail.com (Rowan Tommins)
On 13 February 2020 03:33:32 GMT+00:00, Mike Schinkel <mike@newclarity.net> wrote:
>> On Feb 12, 2020, at 5:47 PM, Rowan Tommins collins@gmail.com> >wrote: >> >> On 12/02/2020 03:58, Mike Schinkel wrote: >>> Returning a_closure_ instead of a string would be providing a >feature we_already_ have instead of one we do_not_ have. >> >> >> Not really, because you still need a way to get the fully-qualified >name of the function. This is not valid: >> >> namespace MyVendor\Something\Foo; >> function bar() {} >> >> \Closure::fromCallable('bar'); # Error: tries to resolve function >'\bar' >> > >Why would ::function return a short name instead of the fully-qualified >name?
I never said it would; the intent of this example was to show what is possible in current PHP, in response to your comment about returning a closure being "a feature we already have". The key new feature being proposed is a way to resolve an unqualified function name based on current namespace and imports, without calling the function. The open question is whether the result of that should be expressed as a string or as a Closure object, but in current PHP the feature doesn't exist at all. Regards, -- Rowan Tommins [IMSoP]
  108544
February 13, 2020 17:06 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 2:33 AM, Rowan Tommins collins@gmail.com> wrote: > > On 13 February 2020 03:33:32 GMT+00:00, Mike Schinkel <mike@newclarity.net> wrote: >>> On Feb 12, 2020, at 5:47 PM, Rowan Tommins collins@gmail.com> >> wrote: >>> >>> On 12/02/2020 03:58, Mike Schinkel wrote: >>>> Returning a_closure_ instead of a string would be providing a >> feature we_already_ have instead of one we do_not_ have. >>> >>> >>> Not really, because you still need a way to get the fully-qualified >> name of the function. This is not valid: >>> >>> namespace MyVendor\Something\Foo; >>> function bar() {} >>> >>> \Closure::fromCallable('bar'); # Error: tries to resolve function >> '\bar' >>> >> >> Why would ::function return a short name instead of the fully-qualified >> name? > > > I never said it would; the intent of this example was to show what is possible in current PHP, in response to your comment about returning a closure being "a feature we already have". > The key new feature being proposed is a way to resolve an unqualified function name based on current namespace and imports, without calling the function. The open question is whether the result of that should be expressed as a string or as a Closure object, but in current PHP the feature doesn't exist at all.
Let me clarify for you what I was trying to say. 1. IF foo::function returns a name string THEN using Closure::fromCallable( foo::function ) can provide a closure. 2. IF foo::function returns a closure THEN how to we get the name string? Hence we already have a way to get the closure _if_ ::function returns a name string, but we would *not* have a way to get the name string if ::function returns a closure. IOW, we can already derive a closure if we have a name, but we cannot derive a name if we have a closure. I hope this clarifies what I was trying to say when I said "we already have it." -Mike
  108546
February 13, 2020 17:26 rowan.collins@gmail.com (Rowan Tommins)
On Thu, 13 Feb 2020 at 17:06, Mike Schinkel <mike@newclarity.net> wrote:

> 1. IF foo::function returns a name string THEN using > Closure::fromCallable( foo::function ) can provide a closure. > > 2. IF foo::function returns a closure THEN how to we get the name string? >
Right, I'm with you now. However, I think the answer people are suggesting to "how do we get the name string?" is "why do we need to?" Or as Chase Peeler more eloquently put it:
> Can anyone think of a use-case where you would want a string name of a > function and a callable would not be acceptable, besides possibly debugging
> code that said 'echo "I'm calling ".myfunction::function;'? Everything that
> I can think of that accepts a function name, also accepts a callable (e.g.
> array_map), but I could be forgetting something.
There's a Venn diagram, essentially, of: a) use cases where a Closure would be useful, but a string wouldn't b) use cases where a string would be useful, but a Closure wouldn't c) use cases where either a string or a Closure would be useful If (and it's a genuine open question) all the use cases fall into categories (a) and (c), we can make the syntax for closures simpler by skipping the "get name" step and making foo::fn return a closure straight away. So the question is, are there use cases that fall into category (b)? Regards, -- Rowan Tommins [IMSoP]
  108547
February 13, 2020 18:19 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 12:26 PM, Rowan Tommins collins@gmail.com> wrote: > Right, I'm with you now. However, I think the answer people are suggesting > to "how do we get the name string?" is "why do we need to?"
1. Say I want to provide users with the ability to build queries and use functions where I want to provide the names of the functions to the users: $qt = QueryTool(); $qt->addFunction(substr::function); $qt->addFunction(add_product::function); $qt->showUI(); 2. Say I want to serialize any configuration that uses functions. You can't serialize closures but you can serialize function names to a database or JSON. 3. You cannot use a closure as an array index so if you want to use the function name as an array key to associate additional information to the use with the function, such as: $qt = QueryTool(); $qt->addFunction(substr::function, array( new FuncParam( 'string', 'string' ), new FuncParam( 'int', 'start', QueryTool::Optional ), new FuncParam( 'int', 'length', QueryTool::Optional ) )); $qt->addFunction(add_product::function, array( new FuncParam( 'string', 'product_id' ), new FuncParam( 'float', 'price' ) )); 4. Being able to compose quality error and warning message that include function names.
> Or as Chase Peeler more eloquently put it: > >> Can anyone think of a use-case where you would want a string name of a >> function and a callable would not be acceptable, besides possibly debugging >> code that said 'echo "I'm calling ".myfunction::function;'? Everything that >> I can think of that accepts a function name, also accepts a callable (e.g. >> array_map), but I could be forgetting something.
Eloquently maybe, but of limited vision.
> There's a Venn diagram, essentially, of: > a) use cases where a Closure would be useful, but a string wouldn't > b) use cases where a string would be useful, but a Closure wouldn't > c) use cases where either a string or a Closure would be useful > > If (and it's a genuine open question) all the use cases fall into > categories (a) and (c), we can make the syntax for closures simpler by > skipping the "get name" step and making foo::fn return a closure straight > away. > > So the question is, are there use cases that fall into category (b)?
Yes. Definitely. But since I seem to be in the minority of caring about the name, let me propose the following which was influenced by Larry Garfield's most recent post. Since it seems that people want the convenience of a short notation to get a closure, how about this: function foo{} foo::function — Returns name of function foo::fn — Returns closure for function Since using `fn` creates anonymous function closures it kinda makes sense that `::fn` would return a closure. -Mike
  108549
February 13, 2020 18:48 larry@garfieldtech.com ("Larry Garfield")
On Thu, Feb 13, 2020, at 12:19 PM, Mike Schinkel wrote:
> > On Feb 13, 2020, at 12:26 PM, Rowan Tommins collins@gmail.com> wrote: > > Right, I'm with you now. However, I think the answer people are suggesting > > to "how do we get the name string?" is "why do we need to?" > > 1. Say I want to provide users with the ability to build queries and > use functions where I want to provide the names of the functions to the > users: > > $qt = QueryTool(); > $qt->addFunction(substr::function); > $qt->addFunction(add_product::function); > $qt->showUI(); > > 2. Say I want to serialize any configuration that uses functions. You > can't serialize closures but you can serialize function names to a > database or JSON. > > 3. You cannot use a closure as an array index so if you want to use the > function name as an array key to associate additional information to > the use with the function, such as: > > $qt = QueryTool(); > $qt->addFunction(substr::function, array( > new FuncParam( 'string', 'string' ), > new FuncParam( 'int', 'start', QueryTool::Optional ), > new FuncParam( 'int', 'length', QueryTool::Optional ) > )); > $qt->addFunction(add_product::function, array( > new FuncParam( 'string', 'product_id' ), > new FuncParam( 'float', 'price' ) > ));
Those are valid examples. I suppose along similar lines would be tooling that uses a builder to generate compiled code. (Eg, if $qt were used to then generate an optimized function in a class on disk.) Flipside: In those cases we really should standardize static methods better than with arrays, yet none of these address (nor can they) instance methods.
> 4. Being able to compose quality error and warning message that include > function names. > > > Or as Chase Peeler more eloquently put it: > > > >> Can anyone think of a use-case where you would want a string name of a > >> function and a callable would not be acceptable, besides possibly debugging > >> code that said 'echo "I'm calling ".myfunction::function;'? Everything that > >> I can think of that accepts a function name, also accepts a callable (e.g. > >> array_map), but I could be forgetting something. > > Eloquently maybe, but of limited vision. > > > > There's a Venn diagram, essentially, of: > > a) use cases where a Closure would be useful, but a string wouldn't > > b) use cases where a string would be useful, but a Closure wouldn't > > c) use cases where either a string or a Closure would be useful > > > > If (and it's a genuine open question) all the use cases fall into > > categories (a) and (c), we can make the syntax for closures simpler by > > skipping the "get name" step and making foo::fn return a closure straight > > away. > > > > So the question is, are there use cases that fall into category (b)? > > Yes. Definitely.
I agree, the above examples demonstrate valid use cases for (b).
> But since I seem to be in the minority of caring about the name, let me > propose the following which was influenced by Larry Garfield's most > recent post. Since it seems that people want the convenience of a > short notation to get a closure, how about this: > > function foo{} > > foo::function — Returns name of function > foo::fn — Returns closure for function > > Since using `fn` creates anonymous function closures it kinda makes > sense that `::fn` would return a closure. > > -Mike
thinking-face-emoji.gif. I could be convinced of that. It seems like "both" is a possible solution, but my concern would be someone using one of them in a case where either works, inadvertently, when the callee is expecting just one. Eg, getting into the habit of using foo::fn, and then using it on a builder routine that chokes "later" when it tries to serialize something. --Larry Garfield
  108550
February 13, 2020 20:12 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 1:48 PM, Larry Garfield <larry@garfieldtech.com> wrote: >> But since I seem to be in the minority of caring about the name, let me >> propose the following which was influenced by Larry Garfield's most >> recent post. Since it seems that people want the convenience of a >> short notation to get a closure, how about this: >> >> function foo{} >> >> foo::function — Returns name of function >> foo::fn — Returns closure for function >> >> Since using `fn` creates anonymous function closures it kinda makes >> sense that `::fn` would return a closure. >> >> -Mike > > thinking-face-emoji.gif. I could be convinced of that. It seems like "both" is a possible solution, but my concern would be someone using one of them in a case where either works, inadvertently, when the callee is expecting just one. Eg, getting into the habit of using foo::fn, and then using it on a builder routine that chokes "later" when it tries to serialize something.
True. But it would be a really high bar to say we can only add new features if we can completely protect the developer from themselves. At some point we have to assume programmers are adults, or at least can take responsibility for learning how the language works. -Mike P.S. OTOH, if the routine that requires ::function and not ::fn were to type hint the parameter, it would choke with an applicable error message.
  108552
February 13, 2020 21:55 larry@garfieldtech.com ("Larry Garfield")
On Thu, Feb 13, 2020, at 2:12 PM, Mike Schinkel wrote:
> > On Feb 13, 2020, at 1:48 PM, Larry Garfield <larry@garfieldtech.com> wrote: > >> But since I seem to be in the minority of caring about the name, let me > >> propose the following which was influenced by Larry Garfield's most > >> recent post. Since it seems that people want the convenience of a > >> short notation to get a closure, how about this: > >> > >> function foo{} > >> > >> foo::function — Returns name of function > >> foo::fn — Returns closure for function > >> > >> Since using `fn` creates anonymous function closures it kinda makes > >> sense that `::fn` would return a closure. > >> > >> -Mike > > > > thinking-face-emoji.gif. I could be convinced of that. It seems like "both" is a possible solution, but my concern would be someone using one of them in a case where either works, inadvertently, when the callee is expecting just one. Eg, getting into the habit of using foo::fn, and then using it on a builder routine that chokes "later" when it tries to serialize something. > > True. > > But it would be a really high bar to say we can only add new features > if we can completely protect the developer from themselves. At some > point we have to assume programmers are adults, or at least can take > responsibility for learning how the language works.
Strawman argument. Nothing can "completely" protect developers from themselves; not even Rust. :-) But features should still be designed in such a way as to be hard to screw up. Not impossible, hard. The question I pose is whether "both" would be "hard enough" to get wrong that it's not going to cause more confusion than it solves. I don't know the answer to that question. --Larry Garfield
  108553
February 13, 2020 22:26 d.h.j.takken@freedom.nl (Dik Takken)
On 13-02-2020 19:19, Mike Schinkel wrote:
> But since I seem to be in the minority of caring about the name, let me propose the following which was influenced by Larry Garfield's most recent post. Since it seems that people want the convenience of a short notation to get a closure, how about this: > > function foo{} > > foo::function — Returns name of function > foo::fn — Returns closure for function > > Since using `fn` creates anonymous function closures it kinda makes sense that `::fn` would return a closure.
That is somewhat confusing in my opinion, the two class constants are too similar. I would rather prefer: foo::function — Returns name of function foo::closure — Returns closure for function Regards, Dik Takken
  108557
February 14, 2020 00:31 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 5:26 PM, Dik Takken takken@freedom.nl> wrote: > > On 13-02-2020 19:19, Mike Schinkel wrote: >> function foo{} >> >> foo::function — Returns name of function >> foo::fn — Returns closure for function >> >> Since using `fn` creates anonymous function closures it kinda makes sense that `::fn` would return a closure. > > That is somewhat confusing in my opinion, the two class constants are > too similar. I would rather prefer: > > foo::function — Returns name of function > foo::closure — Returns closure for function > > Regards, > Dik Takken
I actually prefer foo::closure over foo::fn though either would be fine with me, especially if it means getting the feature vs. not getting the feature. The reason I proposed ::fn was because I trying to suggest something that I though Larry Garfield would appreciate given his recent comment[1] in response to you where he said: "Analysis: I stand by my earlier statement that ::function is just too damned long for this funtionality. Not when already reserved shorter options exist. ::fn" -Mike [1] https://externals.io/message/108459#108542
  108555
February 14, 2020 00:24 rowan.collins@gmail.com (Rowan Tommins)
On 13 February 2020 18:19:08 GMT+00:00, Mike Schinkel <mike@newclarity.net> wrote:
>Eloquently maybe, but of limited vision.
I think that's a rather negative way of putting it; there was a request for use cases, and you have supplied some, so thank you. :) An idea I had earlier which might solve some of them is if what was returned was not a normal Closure instance, but a new class like FunctionReference. It could then "remember" the name of the function wrapped, and implement __toString, Serializable, etc. It could inherit from Closure, so instanceof checks would work, and bindTo would return a normal Closure. I'm sure there's downsides I haven't thought of yet, but I thought I'd throw the idea into the mix. A possible future direction would then be to have ::class return some kind of ClassRef object, with the obvious downside that it would no longer pass string type hints without casting. I'm also not sure what the object would do, other than feel nicer from a type system point of view. Regards, -- Rowan Tommins [IMSoP]
  108558
February 14, 2020 00:39 mike@newclarity.net (Mike Schinkel)
> On Feb 13, 2020, at 7:24 PM, Rowan Tommins collins@gmail.com> wrote: > > An idea I had earlier which might solve some of them is if what was returned was not a normal Closure instance, but a new class like FunctionReference. It could then "remember" the name of the function wrapped, and implement __toString, Serializable, etc. It could inherit from Closure, so instanceof checks would work, and bindTo would return a normal Closure. I'm sure there's downsides I haven't thought of yet, but I thought I'd throw the idea into the mix.
I thought about that too, and mentioned it yesterday in a reply[1] to you on this list. Here is the link to the Gist with the hypothetical code using such a concept: - https://gist.github.com/mikeschinkel/78684d708358e1d101e319c7a2fdef9c -Mike [1] https://www.mail-archive.com/internals@lists.php.net/msg100719.html
  108559
February 14, 2020 07:04 manuelcanga@gmail.com (Manuel Canga)
On Fri, 14 Feb 2020 at 01:39, Mike Schinkel <mike@newclarity.net> wrote:

> > On Feb 13, 2020, at 7:24 PM, Rowan Tommins collins@gmail.com> > wrote: > > > > An idea I had earlier which might solve some of them is if what was > returned was not a normal Closure instance, but a new class like > FunctionReference. It could then "remember" the name of the function > wrapped, and implement __toString, Serializable, etc. It could inherit from > Closure, so instanceof checks would work, and bindTo would return a normal > Closure. I'm sure there's downsides I haven't thought of yet, but I thought > I'd throw the idea into the mix. > > I thought about that too, and mentioned it yesterday in a reply[1] to you > on this list. > > Here is the link to the Gist with the hypothetical code using such a > concept: > > - https://gist.github.com/mikeschinkel/78684d708358e1d101e319c7a2fdef9c > > -Mike > > > [1] https://www.mail-archive.com/internals@lists.php.net/msg100719.html > > Maybe, bettern fn: * fn(callable $to_clsoure ): *closure ?
I know fn is used in arrow functions. However, for that same reason fn is the most convenient. Using dik examples( by the way, they are brilliant ): $result = Stats::of($string) ->analyze(fn(normalizeNewlines)) ->analyze(fn(readingLevel)) ->analyze(fn(countStats)) ->analyze(fn($synonymSuggester, 'analyze')) ->analyze(fn(WordCouter::class, 'analyze')) ->analyze(fn($s) => wordDistribution($s, 3)) Returning to ::function again and with a modified example of dik( again ): Now is: result = Stats::of($string) ->analyze('\Stats\Analyzer\normalizeNewlines') ->analyze('\Stats\Readings\readingLevel') ->analyze('\Stats\Parser\countStats') ->analyze(fn($s) => \Stats\Analyzer\wordDistribution($s, 3)); Could be: use function Stats\Analyzer\normalizeNewlines; use function \Stats\Readings\readingLevel; use function \Stats\Parser\countStats; result = Stats::of($string) ->analyze(normalizeNewlines::function) ->analyze(readingLevel::function) ->analyze(countStats::function ) ->analyze(fn($s) => \Stats\Analyzer\wordDistribution($s, 3)); Maybe '::function' is something long, however it wiil be autocompleted by editors and, moreover, nowadays function is a reserved keyword. Could we use "normalizeNewlines::name", but in this case: normalizeNewlines is function or class ?, because in this case could be "normalizeNewlines" a class and "name" a constant of class. Rowan put me in an awkward situation with [MyClass::class, method::function ] however, in this case, could be simply MyClass::class, 'method' ]. Because method don't need be used with namespace. ::function could be documented with advice of being used with name of functions. Because ::function is also useful in array: $filters = [ normalizeNewlines::function, readingLevel::function, countStats::function ] $content = Stats::of($string); foreach($filters as $filter) $filter($content); Regards
  108593
February 15, 2020 08:22 larry@garfieldtech.com ("Larry Garfield")
On Fri, Feb 14, 2020, at 1:04 AM, Manuel Canga wrote:

> Maybe, bettern fn: * fn(callable $to_clsoure ): *closure ? > > I know fn is used in arrow functions. However, for that same reason fn is > the most convenient. > > Using dik examples( by the way, they are brilliant ):
Point of order: The Stats analyzer examples were from me, not Dik. :-) They're taken from one of the monad chapters in the book I'm working on. --Larry Garfield
  108594
February 15, 2020 11:51 php@manuelcanga.dev (Manuel Canga)
---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield <larry@garfieldtech.com> escribió ----
 > On Fri, Feb 14, 2020, at 1:04 AM, Manuel Canga wrote:
 > 
 > > Maybe, bettern fn:  * fn(callable $to_clsoure ): *closure ?
 > > 
 > > I know fn is used in arrow functions. However, for that same reason fn is
 > > the most convenient.
 > > 
 > > Using dik examples( by the way, they are brilliant ):
 > 
 > Point of order: The Stats analyzer examples were from me, not Dik. :-)  They're taken from one of the monad chapters in the book I'm working on.
 > 
 > --Larry Garfield


Oops!, sorry Larry!. Then you book will be securily a great book.
  108595
February 15, 2020 12:07 php@manuelcanga.dev (Manuel Canga)
---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield <larry@garfieldtech.com> escribió ----
 > On Fri, Feb 14, 2020, at 1:04 AM, Manuel Canga wrote:
 > 
 > > Maybe, bettern fn:  * fn(callable $to_clsoure ): *closure ?
 > > 
 > > I know fn is used in arrow functions. However, for that same reason fn is
 > > the most convenient.
 > > 
 > > Using dik examples( by the way, they are brilliant ):
 > 
 > Point of order: The Stats analyzer examples were from me, not Dik. :-)  They're taken from one of the monad chapters in the book I'm working on.
 > 
 > --Larry Garfield
 > 

By the way...the following is for folks in internals:

Do you think it is worth creating an RFC page about :: function ? I say that because I see that there are many voices against of :: function
  108601
February 15, 2020 17:01 larry@garfieldtech.com ("Larry Garfield")
On Sat, Feb 15, 2020, at 6:07 AM, Manuel Canga wrote:
> > ---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield > <larry@garfieldtech.com> escribió ---- > > On Fri, Feb 14, 2020, at 1:04 AM, Manuel Canga wrote: > > > > > Maybe, bettern fn: * fn(callable $to_clsoure ): *closure ? > > > > > > I know fn is used in arrow functions. However, for that same > reason fn is > > > the most convenient. > > > > > > Using dik examples( by the way, they are brilliant ): > > > > Point of order: The Stats analyzer examples were from me, not Dik. > :-) They're taken from one of the monad chapters in the book I'm > working on. > > > > --Larry Garfield > > > > By the way...the following is for folks in internals: > > Do you think it is worth creating an RFC page about :: function ? I say > that because I see that there are many voices against of :: function
There seems to be little pushback on the idea of a better way to name/reference functions, so an RFC page for that seems reasonable. What the syntax is, that's still in heavy bikeshed territory, so maybe don't name the RFC based on the implementation detail yet. :-) I'd love to get some input from Nikita or Dimitry or someone else with way more parser experience on how feasible any of the options discussed so far would be; if some are vastly more straightforward than others, that carries a lot of weight. --Larry Garfield
  108611
February 15, 2020 23:25 d.h.j.takken@freedom.nl (Dik Takken)
On 15-02-2020 13:07, Manuel Canga wrote:
> Do you think it is worth creating an RFC page about :: function ? I say that because I see that there are many voices against of :: function
Let us go back to the start of this thread. Your idea was to simplify array_map('\My\I18N\i18n_translate', $array) to array_map(i18n_translate::func, $array) Then the discussion took the topic one level deeper. In your examples, you seek a better way to pass a function as callable. In PHP a callable can be: 1. a string 2. an array 3. a closure Then it was suggested that a short syntax to wrap the function into a closure is more useful. Using strings to refer to functions has consistency issues and the proposed ::func does not solve them. Also, the array syntax for referring to methods could use a better alternative. All things considered, I think the originally proposed feature, using ::func to get the function name, has limited use. Having a ::func (or ::function or ::fn) return a closure might be somewhat controversial and there is no consensus about the name. The only way to know how this will be received is to write an RFC and call for a vote. My own guess would be that an 'enclosure' construct along the lines of array_map({i18n_translate}, $array) array_map({$object->method}, $array) has better chances of succeeding but you never know what happens. Perhaps you could team up with Michał and use the feedback from this thread to produce an RFC. Regards, Dik Takken
  108560
February 14, 2020 08:23 rowan.collins@gmail.com (Rowan Tommins)
On 14 February 2020 00:39:15 GMT+00:00, Mike Schinkel <mike@newclarity.net> wrote:
>> On Feb 13, 2020, at 7:24 PM, Rowan Tommins collins@gmail.com> >wrote: >> >> An idea I had earlier which might solve some of them is if what was >returned was not a normal Closure instance, but a new class like >FunctionReference. It could then "remember" the name of the function >wrapped, and implement __toString, Serializable, etc. It could inherit >from Closure, so instanceof checks would work, and bindTo would return >a normal Closure. I'm sure there's downsides I haven't thought of yet, >but I thought I'd throw the idea into the mix. > >I thought about that too, and mentioned it yesterday in a reply[1] to >you on this list. > >Here is the link to the Gist with the hypothetical code using such a >concept: > >- https://gist.github.com/mikeschinkel/78684d708358e1d101e319c7a2fdef9c
What I had in mind was a combination of that and the existing Closure class, so: $ref = foo::fn; $ref(); // run the function $ref->name; // access extra metadata $ref->bindTo($whatever); // get a new Closure with a bound context That would also combine well with one of the proposed bracket style syntaxes that let you specify methods more naturally: {foo}->name; // qualified name of local function {Foo::bar}; // closure for a static method {$this->baz}->bindTo($that); // closure for a method of current class rebound to a different object Regards, -- Rowan Tommins [IMSoP]