Hi internals,
I Would like to present a possible new "::func resolution" for your
consideration.
In first place, PHP now support "::class" in this way:
use My\I18N;
$mapped_array = array_map([I18N::class, 'translate'], $array);
It avoid add Full I18N namespace in callback.
However with functions is different:
use function \My\I18N\i18n_translate;
$mapped_array = array_map('\My\I18N\i18n_translate', $array);
What is the useful here of importing the function?.
My proposal is ":func" in order to avoid full namespace in callback of
functions. E.g:
use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
"<string>::func" should validate if a function with <string>
is imported.
In this case, "<string>::func" is replaced with FQN of this function,
otherwise with only "<string>"
What is your opinion ? Do you see it useful ?
Thanks and I'm sorry for my English( I'm a Spanish ).
Regards
I'm sorry. I forgot subject due to nerves. I add it.
Hi internals,
I Would like to present a possible new "::func resolution" for your consideration.In first place, PHP now support "::class" in this way:
use My\I18N;
$mapped_array = array_map([I18N::class, 'translate'], $array);
It avoid add Full I18N namespace in callback.However with functions is different:
use function \My\I18N\i18n_translate;
$mapped_array = array_map('\My\I18N\i18n_translate', $array);
What is the useful here of importing the function?.
My proposal is ":func" in order to avoid full namespace in callback of functions. E.g:use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
"<string>::func" should validate if a function with
<string>
is imported. In this case, "<string>::func" is replaced with FQN of this function, otherwise with only "<string>"What is your opinion ? Do you see it useful ?
Thanks and I'm sorry for my English( I'm a Spanish ).
Regards
Hi Manuel
Thanks for bringing this idea forward.
Adding a non-string construct to reference functions uniquely seems
like a good idea and would be consistent with the existing ::class
keyword for classes.
I'm wondering if it would be useful to take a page out of the book of
other programming languages in order to shorten the notation further:
// First file.
namespace A\B;function foo() { }
// Second file.
use function A\B\foo;function bar(callable $c) { }
// Current approach.
bar('\A\B\foo');// Notation without parantheses, references imported function.
// Similarly, in the future, Class::method could reference a
// class method.
bar(foo);// Notation with references, could be expanded in the future
// for static class methods, such as &Class::method.
bar(&foo);
The second example can already be achieved by defining your own
constant that references the fully qualified name and importing it.
However, an explicit, separate, import of these constants is still
needed, which is not ideal.
A suffix "::func" would also be useful, but allowing shorter notation
would improve readability when using the functional programming
paradigm and composing functions.
Op di, feb 11, 2020 at 12:13 schreef Manuel Canga
manuelcanga@gmail.com:
Hi internals,
I Would like to present a possible new "::func resolution" for your
consideration.In first place, PHP now support "::class" in this way:
use My\I18N;
$mapped_array = array_map([I18N::class, 'translate'], $array);
It avoid add Full I18N namespace in callback.However with functions is different:
use function \My\I18N\i18n_translate;
$mapped_array = array_map('\My\I18N\i18n_translate', $array);
What is the useful here of importing the function?.
My proposal is ":func" in order to avoid full namespace in callback of
functions. E.g:use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
"<string>::func" should validate if a function with
<string>
is
imported.
In this case, "<string>::func" is replaced with FQN of this function,
otherwise with only "<string>"What is your opinion ? Do you see it useful ?
Thanks and I'm sorry for my English( I'm a Spanish ).
Regards
Hi Tom,
I thought about that short version. However, in order to avoid
conflicts with constants, I chosen "::func". If this is not problem in
low level of PHP, then I agree with you in shorter version is better.
Thanks for your opinion, Tom
Hi Manuel
Thanks for bringing this idea forward.
Adding a non-string construct to reference functions uniquely seems
like a good idea and would be consistent with the existing ::class
keyword for classes.I'm wondering if it would be useful to take a page out of the book of
other programming languages in order to shorten the notation further:// First file.
namespace A\B;function foo() { }
// Second file.
use function A\B\foo;function bar(callable $c) { }
// Current approach.
bar('\A\B\foo');// Notation without parantheses, references imported function.
// Similarly, in the future, Class::method could reference a
// class method.
bar(foo);// Notation with references, could be expanded in the future
// for static class methods, such as &Class::method.
bar(&foo);The second example can already be achieved by defining your own
constant that references the fully qualified name and importing it.
However, an explicit, separate, import of these constants is still
needed, which is not ideal.A suffix "::func" would also be useful, but allowing shorter notation
would improve readability when using the functional programming
paradigm and composing functions.Op di, feb 11, 2020 at 12:13 schreef Manuel Canga
manuelcanga@gmail.com:Hi internals,
I Would like to present a possible new "::func resolution" for your
consideration.In first place, PHP now support "::class" in this way:
use My\I18N;
$mapped_array = array_map([I18N::class, 'translate'], $array);
It avoid add Full I18N namespace in callback.However with functions is different:
use function \My\I18N\i18n_translate;
$mapped_array = array_map('\My\I18N\i18n_translate', $array);
What is the useful here of importing the function?.
My proposal is ":func" in order to avoid full namespace in callback of
functions. E.g:use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
"<string>::func" should validate if a function with
<string>
is
imported.
In this case, "<string>::func" is replaced with FQN of this function,
otherwise with only "<string>"What is your opinion ? Do you see it useful ?
Thanks and I'm sorry for my English( I'm a Spanish ).
Regards
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'].
Also for keeping consistency and avoiding new keywords wouldn't it fit better as
::function instead of ::func?
Best regards.
Diogo
Hi, Diogo
That's right. I could be useful with classes as well.
I was thinking about 'function' or 'func'. 'function' is more
semantic, but 'func' is used with "short functions"( PHP 7.4 ) and
these will be used for callbacks. Then I think 'func' will be more
consistent.
Thanks, Diogo,
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'].
Also for keeping consistency and avoiding new keywords wouldn't it fit better as
::function instead of ::func?Best regards.
Diogo
Hi,
I was thinking about 'function' or 'func'. 'function' is more
semantic, but 'func' is used with "short functions"( PHP 7.4 ) and
these will be used for callbacks. Then I think 'func' will be more
consistent.
Actually PHP 7.4's short closures / array functions use fn
, not func
.
But ::fn
would look... weird.
I agree with Diogo that ::function
would be more consistent.
--
Guilliam Xavier
Ups!. What big mistake!
Then ::function should be a better option.
Thanks, Guilliam,
Hi,
I was thinking about 'function' or 'func'. 'function' is more
semantic, but 'func' is used with "short functions"( PHP 7.4 ) and
these will be used for callbacks. Then I think 'func' will be more
consistent.Actually PHP 7.4's short closures / array functions use
fn
, notfunc
.
But::fn
would look... weird.
I agree with Diogo that::function
would be more consistent.--
Guilliam Xavier
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).
Nicolas
On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas
nicolas.grekas+php@gmail.com wrote:
Le mar. 11 févr. 2020 à 12:52, Diogo Galvao diogo86@gmail.com a écrit :
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_name>::class is used, class with <class_name>
can or cannot exists in that moment.
Using <function_name>::func( or <function_name>::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
Thanks, Nicolas,
On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas
nicolas.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_name>::class is used, class with <class_name>
can or cannot exists in that moment.
Using <function_name>::func( or <function_name>::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
On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas
nicolas.grekas+php@gmail.com wrote:Le mar. 11 févr. 2020 à 12:52, Diogo Galvao diogo86@gmail.com a écrit :
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_name>::class is used, class with <class_name>
can or cannot exists in that moment.
Using <function_name>::func( or <function_name>::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 consistentChecking 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
Thanks, Nikita. I don't know that RFC.
In that case, why don't use ::callable ?. This will be useful for
functions and others callable. E.g:
enqueue_task([I18n::class, 'translate']::callable);
enqueue_task(translate::callable);
Would it be hard to implement in PHP ?
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
nicolas.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_name>::class is used, class with <class_name>
can or cannot exists in that moment.
Using <function_name>::func( or <function_name>::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 consistentChecking whether the function exists is really unavoidable for functions,
because you don't know whethernamespace 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
On Tue, 11 Feb 2020 at 13:16, Nicolas Grekas
nicolas.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_name>::class is used, class with <class_name>
can or cannot exists in that moment.
Using <function_name>::func( or <function_name>::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 consistentChecking whether the function exists is really unavoidable for functions,
because you don't know whethernamespace 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,
NikitaCan 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,
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:
Or as proposed elsewhere in the thread:
$(I18N::translate)
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
El mié., 12 feb. 2020 22:36, Rowan Tommins rowan.collins@gmail.com
escribió:
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: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 {
}
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]
El mié., 12 feb. 2020 23:01, Rowan Tommins rowan.collins@gmail.com
escribió:
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:
- Change import
- Add namespace to class Controller
In your example, you has the same options:
- Change import
- 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.
El mié., 12 feb. 2020 23:01, Rowan Tommins rowan.collins@gmail.com
escribió:
In your example, you has the same options:
- Change import
- 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]
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 rowan.collins@gmail.com
escribió:
In your example, you has the same options:
- Change import
- 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.
On Thu, 13 Feb 2020 at 08:58, Rowan Tommins rowan.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 rowan.collins@gmail.com
escribió:
In your example, you has the same options:
- Change import
- 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]
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?
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
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 severalx
symbols (of distinct kinds) in scope?
Excellent point.
-Mike
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.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
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:
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
On Feb 12, 2020, at 5:47 PM, Rowan Tommins rowan.collins@gmail.com
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]
On Feb 12, 2020, at 5:47 PM, Rowan Tommins rowan.collins@gmail.com
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.
-
IF foo::function returns a name string THEN using Closure::fromCallable( foo::function ) can provide a closure.
-
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
IF foo::function returns a name string THEN using
Closure::fromCallable( foo::function ) can provide a closure.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]
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?"
- 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();
-
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.
-
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' )
));
- 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 usefulIf (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
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?"
- 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();
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.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.
- 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 usefulIf (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 functionSince 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
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 functionSince 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.
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 functionSince 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
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 functionSince 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
function foo{}
foo::function — Returns name of function
foo::fn — Returns closure for functionSince 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 functionRegards,
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"
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]
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:
-Mike
[1] internals@lists.php.net/msg100719.html" rel="nofollow" target="_blank">https://www.mail-archive.com/internals@lists.php.net/msg100719.html
On Feb 13, 2020, at 7:24 PM, Rowan Tommins rowan.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:-Mike
[1] internals@lists.php.net/msg100719.html" rel="nofollow" target="_blank">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
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
---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield larry@garfieldtech.com escribió ----
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.
---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield larry@garfieldtech.com escribió ----
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
---- En sáb, 15 feb 2020 09:22:41 +0100 Larry Garfield
larry@garfieldtech.com escribió ----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
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:
- a string
- an array
- 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
On Feb 13, 2020, at 7:24 PM, Rowan Tommins rowan.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:
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]
btw it's probably worth mentioning the other reason I didn't pursue
https://wiki.php.net/rfc/consistent_callables.
By itself, the callable type isn't much use. It only allows you to
specify that a type can be called, but it doesn't allow you to specify
what parameters the callable should take, or what the return type
should be.
Just making the callable type be consistent would be a lot of work,
and possibly cause a lot of BC breaks and still not do the useful
thing.
Instead what would be far more useful would be allow people to define
the callable signature, and use that as a type.
callable some_callback(int $x, string $y) : int;
function bar(some_callback $fn) { }
$fn_correct = function (int $x, string $y) : int {}
$fn_wrong = function (int $x, string $y) : int {}
Calling bar with $fn_correct should work.
Calling bar with $fn_wrong should give a type error.
In my opinion, pursuing that idea would be a lot more valuable than
breaking BC for callables without much gain.
cheers
Dan
Ack
btw it's probably worth mentioning the other reason I didn't pursue
https://wiki.php.net/rfc/consistent_callables.By itself, the callable type isn't much use. It only allows you to
specify that a type can be called, but it doesn't allow you to specify
what parameters the callable should take, or what the return type
should be.Just making the callable type be consistent would be a lot of work,
and possibly cause a lot of BC breaks and still not do the useful
thing.Instead what would be far more useful would be allow people to define
the callable signature, and use that as a type.callable some_callback(int $x, string $y) : int;
function bar(some_callback $fn) { }
$fn_correct = function (int $x, string $y) : int {}
$fn_wrong = function (int $x, string $y) : int {}Calling bar with $fn_correct should work.
Calling bar with $fn_wrong should give a type error.In my opinion, pursuing that idea would be a lot more valuable than
breaking BC for callables without much gain.cheers
Dan
Ack
Hi, internals,
I think, we are taking this very far. In Spain, we usually say: "Quien
mucho abarca, poco aprieta". In English, I think is similar to: "don’t bite
off more than you can chew" or KISS/YAGNI in development. IMO, we should
limit RFC to '::function'( scope and accept/reject ). After this, other RFC
can be added/checked like Short closures( of Michal ) or other purposes
with other variation like :interface and ::trait: ( of Mike )
Respect to ::function:
<function_name>::function, it should return a string like ::class. This is:
Case 1: <function_name>::function === \namespace\function_name ( when
function_name is imported )
Case 2: <function_name>::function === \current_namespace\function_name(
when <function_name>::function is below a namespace )
Case 3: <function_name>::function === \function_name( when
<function_name>::function is not below a namespace )
Case 4: \namespace<function_name>::function === \namespace\function_name
Case 5: <function_name>::function === \function_name ( equal of 4 but in
global context )
Examples:
namespace my_project;
use function \vendor\I18n\translate;
$mapped_array = array_map(translate::function, $array);
//= $mapped_array = array_map('\vendor\I18n\translate', $array);
namespace my_project;
$mapped_array = array_map(translate::function, $array);
//= $mapped_array = array_map('\my_project\translate' $array);
$mapped_array = array_map(translate::function, $array);
//= $mapped_array = array_map('\translate' $array);
$mapped_array = array_map(\vendor\I18n\translate::function, $array);
//= $mapped_array = array_map('\vendor\I18n\translate' $array);
$mapped_array = array_map(\translate::function, $array);
//= $mapped_array = array_map('\translate' $array);
Although, cases 3, 4 or 5, can appear useless, however editors could
distinguish
when is a function( and not a ordinary string ) and validate if function
exists or not.
Regards
IMO, we should limit RFC to '::function'( scope and accept/reject ).
Agreed.
After this, other RFC can be added/checked like Short closures( of Michal )
or other purposes with other variation like :interface and ::trait: ( of Mike )
This feels a bit Handmaid's Tale-esque.
But I digress... :-)
-Mike
Nicolas Grekas wrote:
I wish this would return a Closure instead, making $foo::function the
equivalent of Closure::fromCallable($foo).
I didn't include the following in that RFC, because I thought it would
be too controversial, but I think it's worth considering a new syntax
for this.
Given the code:
function foo();
class Zoq {
public function Fot() {}
public static function Pik() {}
}
$obj = new Zoq();
Then these:
$(foo);
$($obj, Fot);
$(Zoq, Fot);
Would be equivalent to:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq', 'Fot']);
Or similar.
The justification for having a dedicated syntax is that I think
readability is quite important in code, and it's reasonably common for
me to have quite long lists of 'callables'.
[Bar::class, foo1::function],
[Bar::class, foo2::function],
[Bar::class, foo3::function],
[Bar::class, foo4::function]
vs
$(Bar, foo1),
$(Bar, foo2),
$(Bar, foo3),
$(Bar, foo4)
The latter is far easier to read for me.
Nikita Popov wrote:
This would circumvent all the issues outlined in
https://wiki.php.net/rfc/consistent_callables.
Probably there would still be some issues with some of the weird stuff
happening internally in SPL related code where the deep horrors
rest...but we can leave them alone...and they might not wake.
cheers
Dan
Ack
Nicolas Grekas wrote:
I wish this would return a Closure instead, making $foo::function the
equivalent of Closure::fromCallable($foo).I didn't include the following in that RFC, because I thought it would
be too controversial, but I think it's worth considering a new syntax
for this.Given the code:
function foo();
class Zoq {
public function Fot() {}
public static function Pik() {}
}
$obj = new Zoq();Then these:
$(foo);
$($obj, Fot);
$(Zoq, Fot);Would be equivalent to:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq', 'Fot']);Or similar.
The justification for having a dedicated syntax is that I think
readability is quite important in code, and it's reasonably common for
me to have quite long lists of 'callables'.[Bar::class, foo1::function],
[Bar::class, foo2::function],
[Bar::class, foo3::function],
[Bar::class, foo4::function]vs
$(Bar, foo1),
$(Bar, foo2),
$(Bar, foo3),
$(Bar, foo4)The latter is far easier to read for me.
Nikita Popov wrote:
This would circumvent all the issues outlined in
https://wiki.php.net/rfc/consistent_callables.Probably there would still be some issues with some of the weird stuff
happening internally in SPL related code where the deep horrors
rest...but we can leave them alone...and they might not wake.cheers
Dan
Ack
Hi, Dan,
I like it, although syntax reminds me to JQuery syntax.
Thanks
Nicolas Grekas wrote:
I wish this would return a Closure instead, making $foo::function the
equivalent of Closure::fromCallable($foo).I didn't include the following in that RFC, because I thought it would
be too controversial, but I think it's worth considering a new syntax
for this.Given the code:
function foo();
class Zoq {
public function Fot() {}
public static function Pik() {}
}
$obj = new Zoq();Then these:
$(foo);
$($obj, Fot);
$(Zoq, Fot);Would be equivalent to:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq', 'Fot']);Or similar.
The justification for having a dedicated syntax is that I think
readability is quite important in code, and it's reasonably common for
me to have quite long lists of 'callables'.[Bar::class, foo1::function],
[Bar::class, foo2::function],
[Bar::class, foo3::function],
[Bar::class, foo4::function]vs
$(Bar, foo1),
$(Bar, foo2),
$(Bar, foo3),
$(Bar, foo4)The latter is far easier to read for me.
Nikita Popov wrote:
This would circumvent all the issues outlined in
https://wiki.php.net/rfc/consistent_callables.Probably there would still be some issues with some of the weird stuff
happening internally in SPL related code where the deep horrors
rest...but we can leave them alone...and they might not wake.cheers
Dan
Ack
Other option:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq', 'Fot']);
to
<Foo> <$foo, Fot> <Zoq::Fot> <Zoq,'Fot>E.g:
array_map(<strlen>, $array);
array_map(<I18n, Translate>, $array);
Do you like ?
wt., 11 lut 2020, 18:42 użytkownik Manuel Canga manuelcanga@gmail.com
napisał:
Nicolas Grekas wrote:
I wish this would return a Closure instead, making $foo::function the
equivalent of Closure::fromCallable($foo).I didn't include the following in that RFC, because I thought it would
be too controversial, but I think it's worth considering a new syntax
for this.Given the code:
function foo();
class Zoq {
public function Fot() {}
public static function Pik() {}
}
$obj = new Zoq();Then these:
$(foo);
$($obj, Fot);
$(Zoq, Fot);Would be equivalent to:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq',
'Fot']);Or similar.
The justification for having a dedicated syntax is that I think
readability is quite important in code, and it's reasonably common for
me to have quite long lists of 'callables'.[Bar::class, foo1::function],
[Bar::class, foo2::function],
[Bar::class, foo3::function],
[Bar::class, foo4::function]vs
$(Bar, foo1),
$(Bar, foo2),
$(Bar, foo3),
$(Bar, foo4)The latter is far easier to read for me.
Nikita Popov wrote:
This would circumvent all the issues outlined in
https://wiki.php.net/rfc/consistent_callables.Probably there would still be some issues with some of the weird stuff
happening internally in SPL related code where the deep horrors
rest...but we can leave them alone...and they might not wake.cheers
Dan
AckOther option:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq',
'Fot']);to
<Foo> <$foo, Fot> <Zoq::Fot> <Zoq,'Fot>E.g:
array_map(<strlen>, $array);
array_map(<I18n, Translate>, $array);Do you like ?
That reminds me an old draft RFC https://wiki.php.net/rfc/short-closures
where I thought curly braces can be used to create closure from syntax
nearly the same as invoking but without parentheses.
That way we can provide clear intent - I mean if whatever is around: curly
braces or $ with parentheses IMHO it should be consistent with call like
format. For example:
$(strlen) or {strlen} for Closure::fromCallable('foo')
$($foo->Fot) or {$foo->Fot} for Closure::fromCallable([$obj, 'Fot'])
$(Zoq::Fot) or {Zoq::Fot} for Closure::fromCallable('Zoq::Fot')
Etc. The same inside like a call but wrapped in something and with no
parentheses part.
Cheers,
Michał Brzuchalski
wt., 11 lut 2020, 18:42 użytkownik Manuel Canga manuelcanga@gmail.com
napisał:That reminds me an old draft RFC https://wiki.php.net/rfc/short-closures
where I thought curly braces can be used to create closure from syntax
nearly the same as invoking but without parentheses.
My message mentioning just about the same idea crossed yours. Thanks for
pointing at this RFC, I think it just caught our attention again. :)
I quickly scanned the list to see if your RFC was discussed and all I
could find was threads about a different RFC with almost identical name
(https://wiki.php.net/rfc/short_closures). Has it not been discussed before?
Regards,
Dik Takken
wt., 11 lut 2020, 21:14 użytkownik Dik Takken d.h.j.takken@freedom.nl
napisał:
wt., 11 lut 2020, 18:42 użytkownik Manuel Canga manuelcanga@gmail.com
napisał:That reminds me an old draft RFC https://wiki.php.net/rfc/short-closures
where I thought curly braces can be used to create closure from syntax
nearly the same as invoking but without parentheses.My message mentioning just about the same idea crossed yours. Thanks for
pointing at this RFC, I think it just caught our attention again. :)I quickly scanned the list to see if your RFC was discussed and all I
could find was threads about a different RFC with almost identical name
(https://wiki.php.net/rfc/short_closures). Has it not been discussed
before?
No, it hasn't been discussed. It stopped at being a draft.
BR,
Michał Brzuchalski
I didn't include the following in that RFC, because I thought it would
be too controversial, but I think it's worth considering a new syntax
for this.Given the code:
function foo();
class Zoq {
public function Fot() {}
public static function Pik() {}
}
$obj = new Zoq();Then these:
$(foo);
$($obj, Fot);
$(Zoq, Fot);Would be equivalent to:
Closure::fromCallable('foo');
Closure::fromCallable([$obj, 'Fot']);
Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq', 'Fot']);Or similar.
Given the fact that $() would only accept functions and methods as
argument, this idea could be taken one step further by writing:
$(foo);
$($obj->Fot);
$(Zoq::Fot);
Referring to a method like this is normally not possible because it is
ambiguous. However, wrapped inside $(), which only accepts functions and
methods, the ambiguity disappears.
The $() syntax is nice and short. And something completely new. As new
syntax can only be 'spent' once, more familiar alternatives should be
explored as well. Thinking about the existing list() and array() syntax,
another possibility could be:
closure(foo);
closure($obj->Fot);
closure(Zoq::Fot);
It is slightly longer but more familiar syntax.
I would love a nicer way to reference function names; it's really ugly
to do functional code in PHP otherwise, or even just dynamic function
logic within a namespace. If I never have to write $fn = NAMESPACE
. '\a_func' again, it will be too soon. :-)
Perhaps Larry can convince us all to go for something like $() by
posting a fabulous functional programming example?
Regards,
Dik Takken
$($obj->Fot);
However, wrapped inside $(), which only accepts
functions and methods
I don't think I meant that. I think it still should accept variables
as well, so it would still be ambiguous as to whether it referred to
the value of the property Fot of object $obj, or the method Fot on the
$obj class.
Also, just in general using something that has the same syntax as
current PHP code, but a different meaning, sounds like a bad idea.
another possibility could be: closure(foo);
That has the downside of exposing the implementation detail, that it's
implemented through closures, which would be a regrettable choice if
we ever decided to change the implementation details in the future.
And it also has the downside of looking like a function call, but
having different rules for what can be put inside it:
function foo();
closure(foo); // fine.
special_closure(foo); // syntax error, undefined constant foo.
Although avoiding new syntax is good in general, if what can go
inside it is different, then I think it requires a new syntax.
cheers
Dan
Ack
$($obj->Fot);
However, wrapped inside $(), which only accepts
functions and methodsI don't think I meant that. I think it still should accept variables
as well, so it would still be ambiguous as to whether it referred to
the value of the property Fot of object $obj, or the method Fot on the
$obj class.
In that case, the ambiguity issue remains. I was thinking about it as a
construct for wrapping functions and methods into closures. Could you
provide an example of how accepting variables would be useful?
another possibility could be: closure(foo);
That has the downside of exposing the implementation detail, that it's
implemented through closures, which would be a regrettable choice if
we ever decided to change the implementation details in the future.
I don't think I understand. Creating a closure is something you do
because of the specific semantics they have. The fact that it yields a
closure is not a detail, it is the reason why I would use it...
And it also has the downside of looking like a function call, but
having different rules for what can be put inside it:function foo();
closure(foo); // fine.
special_closure(foo); // syntax error, undefined constant foo.
That is a downside indeed.
Regards,
Dik Takken
napisał:
>
> > I didn't include the following in that RFC, because I thought it would
> > be too controversial, but I think it's worth considering a new syntax
> > for this.
> >
> > Given the code:
> >
> > function foo();
> > class Zoq {
> > public function Fot() {}
> > public static function Pik() {}
> > }
> > $obj = new Zoq();
> >
> >
> > Then these:
> >
> > $(foo);
> > $($obj, Fot);
> > $(Zoq, Fot);
> >
> > Would be equivalent to:
> >
> > Closure::fromCallable('foo');
> > Closure::fromCallable([$obj, 'Fot']);
> > Closure::fromCallable('Zoq::Fot'); or Closure::fromCallable(['Zoq',
> 'Fot']);
> >
> > Or similar.
>
> Given the fact that $() would only accept functions and methods as
> argument, this idea could be taken one step further by writing:
>
> $(foo);
> $($obj->Fot);
> $(Zoq::Fot);
>
> Referring to a method like this is normally not possible because it is
> ambiguous. However, wrapped inside $(), which only accepts functions and
> methods, the ambiguity disappears.
>
> The $() syntax is nice and short. And something completely new. As new
> syntax can only be 'spent' once, more familiar alternatives should be
> explored as well. Thinking about the existing list() and array() syntax,
> another possibility could be:
>
> closure(foo);
> closure($obj->Fot);
> closure(Zoq::Fot);
>
It looks like a function but it's not a function cause what you have inside
parentheses looks like a const, property and class const.
IMO a statement like that for consistency it should be with no parentheses
like:
$foo = closure foo;
$foo = closure $obj->Fot;
$foo = closure Zoq::Fot;
Cheers,
--
Michał Brzuchalski
I would love a nicer way to reference function names; it's really ugly
to do functional code in PHP otherwise, or even just dynamic function
logic within a namespace. If I never have to write $fn = NAMESPACE
. '\a_func' again, it will be too soon. :-)Perhaps Larry can convince us all to go for something like $() by
posting a fabulous functional programming example?
I walked right into that one, didn't I...
Well, Dik asked me to post a "fabulous functional programming example". I dont' have one, so I'll go with one from the book I'm working on instead. :-)
This is what I have now, assuming all global functions:
$result = Stats::of($string)
->analyze('normalizeNewlines')
->analyze('readingLevel')
->analyze('countStats')
->analyze(fn($s) => wordDistribution($s, 3))
;
I think we are all in agreement that is sub-optimal. If using functions in the Stats\Analyzer
namespace, you end up with this:
$result = Stats::of($string)
->analyze('\Stats\Analyzer\normalizeNewlines')
->analyze('\Stats\Analyzer\readingLevel')
->analyze('\Stats\Analyzer\countStats')
->analyze(fn($s) => \Stats\Analyzer\wordDistribution($s, 3))
;
Or if you're in that namespace (as I often am) you can do:
$ns = NAMESPACE;
$result = Stats::of($string)
->analyze($ns . '\normalizeNewlines')
->analyze($ns . '\readingLevel')
->analyze($ns . '\countStats')
->analyze(fn($s) => \Stats\Analyzer\wordDistribution($s, 3))
;
This is doubleplusungood.
I'll expand the example to use some methods, too, for variety's sake:
$result = Stats::of($string)
->analyze('normalizeNewlines')
->analyze('readingLevel')
->analyze('countStats')
->analyze([$synonymSuggester, 'analyze'])
->analyze([WordCouter::class, 'analyze'])
->analyze(fn($s) => wordDistribution($s, 3))
;
As I understand it, the goal in this thread is to:
- Make code like the above more statically analyziable by not using strings. (That covers a number of areas.)
- Make code like the above easier to write by using symbols rather than strings.
In each case, the analyze() method wants a callable with a specific signature. Enforced callable signatures are absolutely something we desperately need, but are not the scope at the moment. In this example, then, that a function name is a string is incidental; what we care about is that it's callable. I don't have a good example off hand for it actually wanting a string, although strings are callables.
So, given this import block:
use Stats\Analyzer\normalizeNewlines;
use Stats\Analyzer\readingLevel;
use Stats\Analyzer\countStats;
use Stats\Analyzer\normalizeNewlines;
use Stats\Analyzer\wordDistribution;
use Stats\Analyzer\Stats;
use Stats\Analyzer\WordCounter;
here's the above example cast into a number of the different proposals floating about this thread:
::function
$result = Stats::of($string)
->analyze(normalizeNewlines::function)
->analyze(readingLevel::function)
->analyze(countStats::function)
->analyze([$synonymSuggester, 'analyze'])
->analyze([WordCouter::class, 'analyze'])
->analyze(fn($s) => wordDistribution($s, 3))
;
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
$result = Stats::of($string)
->analyze(normalizeNewlines::fn)
->analyze(readingLevel::fn)
->analyze(countStats::fn)
->analyze([$synonymSuggester, 'analyze'])
->analyze([WordCouter::class, 'analyze'])
->analyze(fn($s) => wordDistribution($s, 3))
;
Analysis: Same thing, less typing.
::name
$result = Stats::of($string)
->analyze(normalizeNewlines::name)
->analyze(readingLevel::name)
->analyze(countStats::name)
->analyze([$synonymSuggester, 'analyze'])
->analyze([WordCouter::name, 'analyze'])
->analyze(fn($s) => wordDistribution($s, 3))
;
Analysis: This one gives us unification between classes and functions. I think that would be good, although it's not a hard requirement for the functionality.
Any of the above could be expanded, potentially, to include methods, like so:
::name, for methods, too
$result = Stats::of($string)
->analyze(normalizeNewlines::name)
->analyze(readingLevel::name)
->analyze(countStats::name)
->analyze([$synonymSuggester, analyze::name])
->analyze([WordCouter::name, analyze::name])
->analyze(fn($s) => wordDistribution($s, 3))
;
However, that requires the parser being able to tell the difference between analyze::name inside a callable array and not. That's... potentially complicated, since you can also just have an array that contains both objects and strings, or objects and callables, especially as objects can also be callables with __invoke().
$() or variations thereof
$result = Stats::of($string)
->analyze($(normalizeNewlines))
->analyze($(readingLevel))
->analyze($(countStats))
->analyze($($synonymSuggester, 'analyze'))
->analyze($(WordCouter::class, 'analyze'))
->analyze(fn($s) => wordDistribution($s, 3))
;
Analysis: I'm not sure I like this one, visually. "($(..))" feels like a lot of sigils soup. It also doesn't offer a way to deal with the method names in the object versions, unless we just assume that bare strings are allowed there, like so:
$result = Stats::of($string)
->analyze($(normalizeNewlines))
->analyze($(readingLevel))
->analyze($(countStats))
->analyze($($synonymSuggester, analyze))
->analyze($(WordCouter::class, analyze))
->analyze(fn($s) => wordDistribution($s, 3))
;
I will say that, given the behavior of ::class now, ::fn or ::name "feel like" they should return a string, whereas $() "feels like" it should return a callable, or something richer than a string. That's naturally subjective but is consistent with ::foo being a constant value and $() being, um, jQuery.
Also suggested was this:
closure()
$result = Stats::of($string)
->analyze(closure(normalizeNewlines))
->analyze(closure(readingLevel))
->analyze(closure(countStats))
->analyze(closure($synonymSuggester->analyze))
->analyze(closure(WordCouter::analyze))
->analyze(fn($s) => wordDistribution($s, 3))
;
Which aside from being verbose really does look an awful lot like a function call, and is how I'd interpret it. That said, a syntax that would recognize $foo->bar
as a callable, not an invocation, and Foo::bar
as a callable, would be really nice.
Of course... I feel compelled to ask why we can't just use bare function names. Treating a bare string as a string has been deprecated for several versions. If we remove that in PHP 8 and instead let it mean constant, then function, then class name, the following would become legal:
$result = Stats::of($string)
->analyze(normalizeNewlines)
->analyze(readingLevel)
->analyze(countStats)
->analyze([$synonymSuggester, analyze])
->analyze([WordCouter, analyze])
->analyze(fn($s) => wordDistribution($s, DISTRIBUTION_LIMIT))
;
Which would be much more in line with how many other languages handle symbol names. (There is likely some engine reason why it's way harder than I make it sound; I'd love to hear what that is so I know not to suggest it again, unless it really is that simple in which case...)
In all, I don't think there's a clear obvious winner here. All of the options have some kind of trade-off. My own preferences lean toward:
- Keep it short; we don't need more long words.
- No more parentheses; in context, there will be ample parentheses already anywhere a function is being referenced as a callable, so let's not drift more toward LISP. (This is why I don't think "well wrap it in fromCallable()" is a good answer.)
- Handling method callables at the same time is not required, but would be really good for a complete solution.
--Larry Garfield
I walked right into that one, didn't I...
You did. :)
Well, Dik asked me to post a "fabulous functional programming example". I dont' have one, so I'll go with one from the book I'm working on instead. :-)
Thanks, much appreciated.
$() or variations thereof
$result = Stats::of($string)
->analyze($(normalizeNewlines))
->analyze($(readingLevel))
->analyze($(countStats))
->analyze($($synonymSuggester, 'analyze'))
->analyze($(WordCouter::class, 'analyze'))
->analyze(fn($s) => wordDistribution($s, 3))
;Analysis: I'm not sure I like this one, visually. "($(..))" feels like a lot of sigils soup. It also doesn't offer a way to deal with the method names in the object versions, unless we just assume that bare strings are allowed there, like so:
$result = Stats::of($string)
->analyze($(normalizeNewlines))
->analyze($(readingLevel))
->analyze($(countStats))
->analyze($($synonymSuggester, analyze))
->analyze($(WordCouter::class, analyze))
->analyze(fn($s) => wordDistribution($s, 3))
;
If the $() construct would only accept methods then it provides an
environment without the usual ambiguity and we can write:
$result = Stats::of($string)
->analyze($(normalizeNewlines))
->analyze($(readingLevel))
->analyze($(countStats))
->analyze($($synonymSuggester->analyze))
->analyze($(WordCouter::analyze))
->analyze(fn($s) => wordDistribution($s, 3));
The RFC of Michał (https://wiki.php.net/rfc/short-closures) would yield:
$result = Stats::of($string)
->analyze({normalizeNewlines})
->analyze({readingLevel})
->analyze({countStats})
->analyze({$synonymSuggester->analyze})
->analyze({WordCouter::analyze})
->analyze(fn($s) => wordDistribution($s, 3));
To me this looks less soup-ish compared to using $().
I will say that, given the behavior of ::class now, ::fn or ::name "feel like" they should return a string, whereas $() "feels like" it should return a callable, or something richer than a string. That's naturally subjective but is consistent with ::foo being a constant value and $() being, um, jQuery.
Although a closure would also be a constant I share your 'feel like'
issue. It might just need getting used to.
Of course... I feel compelled to ask why we can't just use bare function names. Treating a bare string as a string has been deprecated for several versions. If we remove that in PHP 8 and instead let it mean constant, then function, then class name, the following would become legal:
$result = Stats::of($string)
->analyze(normalizeNewlines)
->analyze(readingLevel)
->analyze(countStats)
->analyze([$synonymSuggester, analyze])
->analyze([WordCouter, analyze])
->analyze(fn($s) => wordDistribution($s, DISTRIBUTION_LIMIT))
;Which would be much more in line with how many other languages handle symbol names. (There is likely some engine reason why it's way harder than I make it sound; I'd love to hear what that is so I know not to suggest it again, unless it really is that simple in which case...)
I guess using bare function names in that context could be supported. I
would prefer getting rid of these awkward array constructs altogether
though and just write $synonymSuggester->analyze in stead. This requires
a context that only accepts methods, which is what $() or {} could provide.
As a side note, I would love to have a name for these closure producing
constructs we are talking about. Maybe: 'enclosure'?
Regards,
Dik Takken
I have three immediate thoughts:
- It should be
fn
orfunction
; reserving a new word even if it's
contextual is pointless here. - It should support methods.
- It should return a closure, not a string. The reason is for
consistency with methods, where we want to capture the scope at the
time the closure is created, not ran. Imagine passing a private method
as a callback; if it was done via[$obj, 'privatemethod']
then it
will fail at runtime, because the scope it's called in doesn't have
access.
On Tue, 11 Feb 2020 at 16:27, Levi Morrison levi.morrison@datadoghq.com
wrote:
I have three immediate thoughts:
- It should be
fn
orfunction
; reserving a new word even if it's
contextual is pointless here.- It should support methods.
- It should return a closure, not a string. The reason is for
consistency with methods, where we want to capture the scope at the
time the closure is created, not ran. Imagine passing a private method
as a callback; if it was done via[$obj, 'privatemethod']
then it
will fail at runtime, because the scope it's called in doesn't have
access.
Hi, Levi,
- Yes
- Yes, I agree
- Wouldn't it be the opposite?. E.g:
class Foo {
static function run_callable(callable $callable) {
$callable();
}
private static function private_function() {
echo "bar";
}
}
Foo::run_callable( [ 'Foo', 'private_function' ] );
Currently, this is valid. However, the following is fatal:
class Foo {
static function run_callable(callable $callable) {
$callable();
}
private static function private_function() {
echo "bar";
}
}
Foo::run_callable(Closure::fromCallable(['Foo', 'private_function']));
The same with functions. If "::function" would retrieve a closure,
some code could breaking
when people switch string function syntax( e.g: 'strlen' ) to
"::function" syntax( strlen::function ).
On other hand, other option is ::callable, This is different to
::function, because it would affect to other
callables, so that, returns could be other( Closure )
I have three immediate thoughts:
- It should be
fn
orfunction
; reserving a new word even if it's
contextual is pointless here.- It should support methods.
- It should return a closure, not a string. The reason is for
consistency with methods, where we want to capture the scope at the
time the closure is created, not ran. Imagine passing a private method
as a callback; if it was done via[$obj, 'privatemethod']
then it
will fail at runtime, because the scope it's called in doesn't have
access.
I would love a nicer way to reference function names; it's really ugly to do functional code in PHP otherwise, or even just dynamic function logic within a namespace. If I never have to write $fn = NAMESPACE . '\a_func' again, it will be too soon. :-)
I agree with Levi on points 2 and 3, for reasons already stated.
For bikeshedding on the syntax to use, I defer to the engine authors who can say what is possible to implement. However, the guideline should be short. If we make referencing a function too verbose, no one will want to use it and readability will be harmed. Double-whammy. That's why I'd say adding ten characters (::function) is a definite no-no.
::fn would work. There was previous discussion of ::name, which could then also apply to classes and objects. That would be acceptable, I think.
Other alternatives like &(funName) et al are also fine if they actually work; Again, I defer to the implementers on that.
But in general, yes please!
--Larry Garfield
I have three immediate thoughts:
- It should be
fn
orfunction
; reserving a new word even if it's
contextual is pointless here.
Note that even though these do not introduce a new keyword it does mean
that either of these would need to become reserved class constant names
just like 'class'. Otherwise foo::fn or foo::function is ambiguous when
both a function and a class named 'foo' are in scope.
Regards,
Dik Takken
Hi internals,
I Would like to present a possible new "::func resolution" for your
consideration.In first place, PHP now support "::class" in this way:
use My\I18N;
$mapped_array = array_map([I18N::class, 'translate'], $array);
It avoid add Full I18N namespace in callback.However with functions is different:
use function \My\I18N\i18n_translate;
$mapped_array = array_map('\My\I18N\i18n_translate', $array);
What is the useful here of importing the function?.
My proposal is ":func" in order to avoid full namespace in callback of
functions. E.g:use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
"<string>::func" should validate if a function with
<string>
is imported.
In this case, "<string>::func" is replaced with FQN of this function,
otherwise with only "<string>"
I proposed ::function on the list a month ago:
I had started by suggesting ::interface and ::trait:
Marcio Almada suggested that we should use a general ::nameof instead:
-Mike
My proposal is ":func" in order to avoid full namespace in callback of
functions. E.g:use function \My\I18N\i18n_translate;
$mapped_array = array_map(i18n_translate::func, $array);
Something that did not occur to me before: Seeing the scope resolution
operator being used on something that is not a class or object looks
strange to me. Especially given the resemblance with the built-in
'class' constant that classes have, this looks like functions being used
as if they are classes...
Regards,
Dik Takken