Hello all,
I've created a feature request issue on GitHub (here: https://github.com/php/php-src/issues/10414), but I have been advised that it's best to post here.
What I would like to introduce/suggest, is the ability to create a closure from a method using the first-class-callable syntax (eg: MyClass::aMethod(...)), for a non-static method, statically.
Currently, the following code causes an error.
class Test {
public function test(): string { return 'test'; }
}
$closure = Test::test(...);
I understand why the error is thrown, but, and I'm unsure of the specifics regarding this, I think we could delay the error until the closure was called. The reason for this, is that closures can be bound, so if you followed on from the code above, you could do the following:
$closure->bindTo(new Test);
$closure();
The above would bind the closure in $closure to the scope of an object, which in this case, is the class that the method belongs to.
The best example I can think, for this, would be when filter a collection of instances. If you were using a collection library, you would currently have something like the following:
$collection->filter(function (Str $string) {
return !$string->empty();
});
Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for binding the closure to the value it is iterating.
I have limited experience with PHPs source, and C in general, but my understanding would be that if we were creating a closure, we would skip the check for the static method. The code responsible for handling the closure call would most require some additional functionality to check if it was bound to a valid instance, returning an error if it isn't, and then returning an error if it isn't bound at all and the method isn't static.
The more I think about it, the more I think this may require a new type of Closure, or at least a runtime applied interface, to help developers determine whether a closure was created using first-class-callable syntax.
I'm interested in feedback on this, and if anyone could point me towards the correct part of the codebase that handles this, I can investigate further. I am happy to create an RFC if others think this viable.
Best Regards,
Ollie Read
Hello all,
I've created a feature request issue on GitHub (here:
https://github.com/php/php-src/issues/10414), but I have been advised
that it's best to post here.What I would like to introduce/suggest, is the ability to create a
closure from a method using the first-class-callable syntax (eg:
MyClass::aMethod(...)), for a non-static method, statically.Currently, the following code causes an error.
class Test { public function test(): string { return 'test'; } } $closure = Test::test(...);
I understand why the error is thrown, but, and I'm unsure of the
specifics regarding this, I think we could delay the error until the
closure was called. The reason for this, is that closures can be bound,
so if you followed on from the code above, you could do the following:$closure->bindTo(new Test); $closure();
The above would bind the closure in $closure to the scope of an object,
which in this case, is the class that the method belongs to.The best example I can think, for this, would be when filter a
collection of instances. If you were using a collection library, you
would currently have something like the following:$collection->filter(function (Str $string) { return !$string->empty(); });
Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for
binding the closure to the value it is iterating.
So you'd implement this yourself elsewhere?
class Str {
public function empty(): bool { ... }
}
I don't see in this example how this is any better than what is already currently possible:
class Str {
public static function empty(Str $s): bool { ... }
}
$collection->filter(Str::empty(...));
I have limited experience with PHPs source, and C in general, but my
understanding would be that if we were creating a closure, we would
skip the check for the static method. The code responsible for handling
the closure call would most require some additional functionality to
check if it was bound to a valid instance, returning an error if it
isn't, and then returning an error if it isn't bound at all and the
method isn't static.The more I think about it, the more I think this may require a new type
of Closure, or at least a runtime applied interface, to help developers
determine whether a closure was created using first-class-callable
syntax.
This is, I think, the important part here, and would be a prerequisite. Right now there's no way (as far as I know) to differentiate a closure that is callable from one that would be callable if it were bound to an object. That's generally not a huge deal in practice as unbound closures are not often used, but what you're suggesting would make them much more likely. Also, a static closure cannot be bound, so you cannot just blindly bind whatever callable you're passed to $this, in your example. (Besides, blindly binding a closure to $this sounds like a great security hole.)
So for some variant of this to work, I think you'd first need to think through how to (easily and without dipping into reflection) determine if a closure object is bindable (static or not) and if it's already bound. Once that's figured out, then we can see what, if any, short-hand way to make a not-yet-bound closure makes sense. (Which could be FCC syntax or not, I don't know.)
--Larry Garfield
You are absolutely correct. I guess the solution would be to handle it differently in this case.
Creating a closure from a static method would be fine, as it creates a static closure, but when attempting to create a static closure from a non-static method, it would instead return a closure that errors if it isn't bound to an appropriate object. You'd most likely want to restrict this to public methods only, which would help with the security issues.
There's already a check there that throws an error, so we can already tell the difference there, but the tricky part will be in the returned closure. Perhaps something like "BindingClosure" that throws the static error when attempting to call it unbound, or better yet, a more descriptive error about it requiring binding.
Would that be feasible?
Hello all,
I've created a feature request issue on GitHub (here:
https://github.com/php/php-src/issues/10414), but I have been advised
that it's best to post here.What I would like to introduce/suggest, is the ability to create a
closure from a method using the first-class-callable syntax (eg:
MyClass::aMethod(...)), for a non-static method, statically.Currently, the following code causes an error.
class Test { public function test(): string { return 'test'; } } $closure = Test::test(...);
I understand why the error is thrown, but, and I'm unsure of the
specifics regarding this, I think we could delay the error until the
closure was called. The reason for this, is that closures can be bound,
so if you followed on from the code above, you could do the following:$closure->bindTo(new Test); $closure();
The above would bind the closure in $closure to the scope of an object,
which in this case, is the class that the method belongs to.The best example I can think, for this, would be when filter a
collection of instances. If you were using a collection library, you
would currently have something like the following:$collection->filter(function (Str $string) { return !$string->empty(); });
Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for
binding the closure to the value it is iterating.So you'd implement this yourself elsewhere?
class Str {
public function empty(): bool { ... }
}I don't see in this example how this is any better than what is already currently possible:
class Str {
public static function empty(Str $s): bool { ... }
}$collection->filter(Str::empty(...));
I have limited experience with PHPs source, and C in general, but my
understanding would be that if we were creating a closure, we would
skip the check for the static method. The code responsible for handling
the closure call would most require some additional functionality to
check if it was bound to a valid instance, returning an error if it
isn't, and then returning an error if it isn't bound at all and the
method isn't static.The more I think about it, the more I think this may require a new type
of Closure, or at least a runtime applied interface, to help developers
determine whether a closure was created using first-class-callable
syntax.This is, I think, the important part here, and would be a prerequisite. Right now there's no way (as far as I know) to differentiate a closure that is callable from one that would be callable if it were bound to an object. That's generally not a huge deal in practice as unbound closures are not often used, but what you're suggesting would make them much more likely. Also, a static closure cannot be bound, so you cannot just blindly bind whatever callable you're passed to $this, in your example. (Besides, blindly binding a closure to $this sounds like a great security hole.)
So for some variant of this to work, I think you'd first need to think through how to (easily and without dipping into reflection) determine if a closure object is bindable (static or not) and if it's already bound. Once that's figured out, then we can see what, if any, short-hand way to make a not-yet-bound closure makes sense. (Which could be FCC syntax or not, I don't know.)
--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php
Best Regards,
Ollie Read
You are absolutely correct. I guess the solution would be to handle it differently in this case.
Creating a closure from a static method would be fine, as it creates a static closure, but when attempting to create a static closure from a non-static method, it would instead return a closure that errors if it isn't bound to an appropriate object. You'd most likely want to restrict this to public methods only, which would help with the security issues.
There's already a check there that throws an error, so we can already tell the difference there, but the tricky part will be in the returned closure. Perhaps something like "BindingClosure" that throws the static error when attempting to call it unbound, or better yet, a more descriptive error about it requiring binding.
Would that be feasible?
Hello all,
I've created a feature request issue on GitHub (here:
https://github.com/php/php-src/issues/10414), but I have been advised
that it's best to post here.What I would like to introduce/suggest, is the ability to create a
closure from a method using the first-class-callable syntax (eg:
MyClass::aMethod(...)), for a non-static method, statically.Currently, the following code causes an error.
class Test { public function test(): string { return 'test'; } } $closure = Test::test(...);
I understand why the error is thrown, but, and I'm unsure of the
specifics regarding this, I think we could delay the error until the
closure was called. The reason for this, is that closures can be bound,
so if you followed on from the code above, you could do the following:$closure->bindTo(new Test); $closure();
The above would bind the closure in $closure to the scope of an object,
which in this case, is the class that the method belongs to.The best example I can think, for this, would be when filter a
collection of instances. If you were using a collection library, you
would currently have something like the following:$collection->filter(function (Str $string) { return !$string->empty(); });
Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for
binding the closure to the value it is iterating.So you'd implement this yourself elsewhere?
class Str {
public function empty(): bool { ... }
}I don't see in this example how this is any better than what is already currently possible:
class Str {
public static function empty(Str $s): bool { ... }
}$collection->filter(Str::empty(...));
I have limited experience with PHPs source, and C in general, but my
understanding would be that if we were creating a closure, we would
skip the check for the static method. The code responsible for handling
the closure call would most require some additional functionality to
check if it was bound to a valid instance, returning an error if it
isn't, and then returning an error if it isn't bound at all and the
method isn't static.The more I think about it, the more I think this may require a new type
of Closure, or at least a runtime applied interface, to help developers
determine whether a closure was created using first-class-callable
syntax.This is, I think, the important part here, and would be a prerequisite. Right now there's no way (as far as I know) to differentiate a closure that is callable from one that would be callable if it were bound to an object. That's generally not a huge deal in practice as unbound closures are not often used, but what you're suggesting would make them much more likely. Also, a static closure cannot be bound, so you cannot just blindly bind whatever callable you're passed to $this, in your example. (Besides, blindly binding a closure to $this sounds like a great security hole.)
So for some variant of this to work, I think you'd first need to think through how to (easily and without dipping into reflection) determine if a closure object is bindable (static or not) and if it's already bound. Once that's figured out, then we can see what, if any, short-hand way to make a not-yet-bound closure makes sense. (Which could be FCC syntax or not, I don't know.)
--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php
Best Regards,
Ollie Read
FWIW, I think we can "throw out" any automatic binding, that just
complicates things.
In my mind, Test::Func(...) should be treated the same as ['Test',
'Func'] or 'Test::Func' until it is called and if some fancy framework
wants to do something special with the closure, it can do so. FWIW, I
didn't even know this syntax checked anything until today. I can think
of a number of cases where delaying the error to actual execution is
beneficial.
- Just because the method/object doesn't exist, doesn't mean it won't
exist by the time it is called -- this is PHP after all. - I can have a file with 100 million of these things without
triggering any autoloading. - PHPStorm currently doesn't trigger this syntax as an error.
Oh, I didn't mean to suggest that it automatically binds.
My second suggestion for how to achieve this does require some sort of automation. If you create a closure from Str::someMethod($arg1, $arg2) where someMethod isn't static, it should create a closure with the signature fn(Str $object, $arg1, $arg2), which would wrap a call to [$object, 'someMethod]($arg1, $arg2).
However, perhaps the simplest solution, and one that could be done realtively easily and quickly, would be to delay the error, and allow static closures to bound with Closure::bind() if they represent a non-static method.
Best Regards,
Ollie Read
Oh, I didn't mean to suggest that it automatically binds.
My second suggestion for how to achieve this does require some sort of
automation. If you create a closure from Str::someMethod($arg1, $arg2)
where someMethod isn't static, it should create a closure with the
signature fn(Str $object, $arg1, $arg2), which would wrap a call to
[$object, 'someMethod]($arg1, $arg2).
I think this starts to wonders in the realm of Partial Function Application
https://wiki.php.net/rfc/partial_function_application
There's definitely similarity, but I would say it sits somewhere between the two. Rather than reference a partial method call, or create a closure for a method, you're delaying a method call. Or rather, referencing a method. We have the ::class pseudo property, so I see this like an equivalent, but for methods.
Oh, I didn't mean to suggest that it automatically binds.
My second suggestion for how to achieve this does require some sort of automation. If you create a closure from Str::someMethod($arg1, $arg2) where someMethod isn't static, it should create a closure with the signature fn(Str $object, $arg1, $arg2), which would wrap a call to [$object, 'someMethod]($arg1, $arg2).
I think this starts to wonders in the realm of Partial Function Application https://wiki.php.net/rfc/partial_function_application
Best Regards,
Ollie Read
There's definitely similarity, but I would say it sits somewhere
between the two. Rather than reference a partial method call, or create
a closure for a method, you're delaying a method call. Or rather,
referencing a method. We have the ::class pseudo property, so I see
this like an equivalent, but for methods.
So what you're really looking for is a shorter way to write this:
foo(fn(Str $s) => $s->beep());
Now you're definitely in the realm of PFA, pipes, and that kind of functionality. And, sadly, Internals has shown a great deal of resistance to that kind of functionality. I have a user-space implementation of pipes that includes utilities to do something similar[1], but most functional languages I know also don't have a really strong native way to "turn around" a call like that.
I'd love a solution for that, but so far it has proven both elusive and controversial.
--Larry Garfield
[1] https://github.com/Crell/fp/blob/master/src/object.php#L28
There's definitely similarity, but I would say it sits somewhere
between the two. Rather than reference a partial method call, or create
a closure for a method, you're delaying a method call. Or rather,
referencing a method. We have the ::class pseudo property, so I see
this like an equivalent, but for methods.So what you're really looking for is a shorter way to write this:
foo(fn(Str $s) => $s->beep());
Now you're definitely in the realm of PFA, pipes, and that kind of functionality. And, sadly, Internals has shown a great deal of resistance to that kind of functionality. I have a user-space implementation of pipes that includes utilities to do something similar[1], but most functional languages I know also don't have a really strong native way to "turn around" a call like that.
I'd love a solution for that, but so far it has proven both elusive and controversial.
--Larry Garfield
[1] https://github.com/Crell/fp/blob/master/src/object.php#L28
--
To unsubscribe, visit: https://www.php.net/unsub.php
Speaking of partial application, it'd be amazing if it were available
with the current closure syntax:
$func = fn($x, $y, $z) => echo $x + $y + $z;
$plus1 = $func(..., 1);
$plus2n1 = $func(2, 1, ...);
$a = $plus1(1, 2);
$b = $plus2n1(5);
I spent a few days last summer trying this out, but couldn't get the
grammar parser to accept it. I think I have a local branch if someone
would be willing to work on it with me. At least point me in the right
direction. I don't have much free time these days, but it is certainly
doable.
Robert Landers
Software Engineer
Utrecht NL
Speaking of partial application, it'd be amazing if it were available
with the current closure syntax:$func = fn($x, $y, $z) => echo $x + $y + $z;
$plus1 = $func(..., 1);
$plus2n1 = $func(2, 1, ...);$a = $plus1(1, 2);
$b = $plus2n1(5);I spent a few days last summer trying this out, but couldn't get the
grammar parser to accept it. I think I have a local branch if someone
would be willing to work on it with me. At least point me in the right
direction. I don't have much free time these days, but it is certainly
doable.
There has been an RFC for this[1] (including an implementation), which
had been declined.
[1] https://wiki.php.net/rfc/partial_function_application
--
Christoph M. Becker
In my mind, Test::Func(...) should be treated the same as ['Test',
'Func'] or 'Test::Func' until it is called and if some fancy framework
wants to do something special with the closure, it can do so. FWIW, I
didn't even know this syntax checked anything until today. I can think
of a number of cases where delaying the error to actual execution is
beneficial.
- Just because the method/object doesn't exist, doesn't mean it won't
exist by the time it is called -- this is PHP after all.- I can have a file with 100 million of these things without
triggering any autoloading.- PHPStorm currently doesn't trigger this syntax as an error.
This ship has long since sailed. Test::func(...) produces a \Callable object, using the Closure::fromCallable() mechanism internally. One important advantage (and why Nikita favored it over most other callable syntaxes) is that it respects scope visibility better than passing around a buncha strings.
Changing that at this point is a major API break that is not going to happen.
--Larry Garfield
You are absolutely correct. I guess the solution would be to handle it
differently in this case.Creating a closure from a static method would be fine, as it creates a
static closure, but when attempting to create a static closure from a
non-static method, it would instead return a closure that errors if it
isn't bound to an appropriate object. You'd most likely want to
restrict this to public methods only, which would help with the
security issues.
Then don't make it a non-static method? Using an object method as a static method has been bad practice for as long as I can remember and an error since sometime recently, I think, so let's not do that.
There's already a check there that throws an error, so we can already
tell the difference there, but the tricky part will be in the returned
closure. Perhaps something like "BindingClosure" that throws the static
error when attempting to call it unbound, or better yet, a more
descriptive error about it requiring binding.Would that be feasible?
By the time the error is thrown is too late. It's a distinction that needs to be made before the bind. So you could do like:
if ($fn->needsBinding()) {
$fn = $fn->bindTo($o);
}
(Or something.)
That said, it sounds like you're talking about something different anyway that is probably unfeasible.
Also, please do not top post.
--Larry Garfield
Hello all,
Hi Ollie,
I've created a feature request issue on GitHub (here: https://github.com/php/php-src/issues/10414), but I have been advised that it's best to post here.
...
I think we could delay the error until the closure was called.
That sounds like a complete non-starter. Adding something to the
language that produces a closure that can't be called would be an
instant new entry for PHPSadness.
Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for binding the
closure to the value it is iterating.
How would the collection library know it was safe to bind the closure
to each value it was iterating over? It sounds really type unsafe.
What I would like to introduce/suggest, is the ability to create a closure
from a method using the first-class-callable syntax...The more I think about it, the more I think this may require a new ...
I think people should say clearly what the problem they are trying to
solve first, before suggesting solutions.
I think there's at least a couple of problems that can be thought about:
i. Although the first-class-callable syntax allowed avoiding string
based programming for most callables, it doesn't support referring to
some things that you would want to call, including both instance
methods, and constructors*.
ii. Representing an instance method requires at least two pieces of
info; which class it belongs to (which can be used to find the
constructor) and the parameters of the method itself. That doesn't
naturally fit into a closure.
iii. The appropriate syntax for referencing a class instance method
without it being string based isn't obvious. Or at least it isn't
obvious to me.
iv. Writing code that for callback methods is longer than it could be,
e.g. as Larry Garfield wrote:
So what you're really looking for is a shorter way to write this:
foo(fn(Str $s) => $s->beep());
At the risk of suggesting an abomination, given a class of:
class Zoq
{
public function __construct(private Fot $fot) {}
public function Pik(string $zebranky): Frungy {...}
}
If it was possible to generate callable for the constructor with:
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
// signature of $fnConstructor is the same as function(Fot $fot): Zoq
Or for individual methods:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
// signature of $fnMethod is the same as function(Zoq $zoq, string $zebranky): Frungy
That sort of looks like a solution to most of the problems I think exist.
For your particular problem, I believe that would allow:
$fnMethod = Closure::fromClassMethod(Str::class, 'empty');
$collection->filter($fnMethod);
Which isn't shorter, but at least allows passing the callables around
with the type inspectable. Though it might be nicer if PHP had the
ability to definte function signatures types.
That sort of solution obviously doesn't address the problem of having
to refer to the class method as a string, but as I said, I don't have
a suggestion for that. Well, other than to use a different syntax.**
cheers
Dan
Ack
- constructors having their own terrible history.
Hello all,
Hi Ollie,
I've created a feature request issue on GitHub (here: https://github.com/php/php-src/issues/10414), but I have been advised that it's best to post here.
...
I think we could delay the error until the closure was called.That sounds like a complete non-starter. Adding something to the
language that produces a closure that can't be called would be an
instant new entry for PHPSadness.Whereas it would be much nicer to have the following:
$collection->filter(Str::empty(...));
In this situation, the collection library would be responsible for binding the
closure to the value it is iterating.How would the collection library know it was safe to bind the closure
to each value it was iterating over? It sounds really type unsafe.What I would like to introduce/suggest, is the ability to create a closure
from a method using the first-class-callable syntax...The more I think about it, the more I think this may require a new ...
I think people should say clearly what the problem they are trying to
solve first, before suggesting solutions.I think there's at least a couple of problems that can be thought about:
i. Although the first-class-callable syntax allowed avoiding string
based programming for most callables, it doesn't support referring to
some things that you would want to call, including both instance
methods, and constructors*.ii. Representing an instance method requires at least two pieces of
info; which class it belongs to (which can be used to find the
constructor) and the parameters of the method itself. That doesn't
naturally fit into a closure.iii. The appropriate syntax for referencing a class instance method
without it being string based isn't obvious. Or at least it isn't
obvious to me.iv. Writing code that for callback methods is longer than it could be,
e.g. as Larry Garfield wrote:So what you're really looking for is a shorter way to write this:
foo(fn(Str $s) => $s->beep());
At the risk of suggesting an abomination, given a class of:
class Zoq
{
public function __construct(private Fot $fot) {}public function Pik(string $zebranky): Frungy {...}
}
If it was possible to generate callable for the constructor with:
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
// signature of $fnConstructor is the same asfunction(Fot $fot): Zoq
Or for individual methods:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
// signature of $fnMethod is the same asfunction(Zoq $zoq, string $zebranky): Frungy
That sort of looks like a solution to most of the problems I think exist.
For your particular problem, I believe that would allow:
$fnMethod = Closure::fromClassMethod(Str::class, 'empty');
$collection->filter($fnMethod);Which isn't shorter, but at least allows passing the callables around
with the type inspectable. Though it might be nicer if PHP had the
ability to definte function signatures types.That sort of solution obviously doesn't address the problem of having
to refer to the class method as a string, but as I said, I don't have
a suggestion for that. Well, other than to use a different syntax.**
I don't think that actually helps. If you have the object already, then making an FCC from it already works:
If you do not have the object already, then we run back into the issue I pointed out before where we need to differentiate a bindable from needs-binding callable, which is already an issue (though one I've not run into). I think that's a prerequisite for adding any dedicated syntax for "make a closure that doesn't work until it gets bound".
--Larry Garfield
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
// signature of $fnConstructor is the same asfunction(Fot $fot): Zoq
Or for individual methods:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
// signature of $fnMethod is the same asfunction(Zoq $zoq, string $zebranky): Frungy
I don't think that actually helps. If you have the object already,
Neither of new functions I was suggesting have the object already.
Only the type.
If you do not have the object already, then we run back into the issue
I pointed out before where we need to differentiate a bindable from
needs-binding callable,
That's not what I suggesting.
Danack wrote:
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
signature of $fnConstructor is the same asfunction(Fot $fot): Zoq
That doesn't need a binding at all.
Danack wrote:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
signature of $fnMethod is the same asfunction(Zoq $zoq, string $zebranky): Frungy
First parameter is the object to be operated on. You wouldn't need to
do any binding. magic would happen inside the function to take care
of that.
I think that's a prerequisite for adding any dedicated syntax
for "make a closure that doesn't work until it gets bound".
To be clear, I don't support anything like that. As I wrote:
Danack wrote:
That sounds like a complete non-starter. Adding something to the
language that produces a closure that can't be called would be an
instant new entry for PHPSadness.
cheers
Dan
Ack
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
// signature of $fnConstructor is the same asfunction(Fot $fot): Zoq
Or for individual methods:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
// signature of $fnMethod is the same asfunction(Zoq $zoq, string $zebranky): Frungy
I don't think that actually helps. If you have the object already,
Neither of new functions I was suggesting have the object already.
Only the type.If you do not have the object already, then we run back into the issue
I pointed out before where we need to differentiate a bindable from
needs-binding callable,That's not what I suggesting.
Danack wrote:
$fnConstructor = Closure::fromClassConstructor(Zoq::class);
signature of $fnConstructor is the same asfunction(Fot $fot): Zoq
That doesn't need a binding at all.
Danack wrote:
$fnMethod = Closure::fromClassMethod(Zoq::class, 'Pik');
signature of $fnMethod is the same asfunction(Zoq $zoq, string $zebranky): Frungy
First parameter is the object to be operated on. You wouldn't need to
do any binding. magic would happen inside the function to take care
of that.
Oh, I see what you're getting at. I missed the signature on first read. So you're saying that would generate a callable equivalent to:
$fnMethod = fn (Zok $zok, string $zebranky): Frungy => $zok->Pik($zebranky);
That's an interesting idea, though probably not self-evident to many people. If we wanted to do that, I'd probably push for something more compact as a syntax, and a bit more auto-refactorable. I think the Ara language had syntax for something like this, $$->Pik(...) ? If I'm remembering correctly, which I may or may not be?
(We're also now rather far afield from the OP's request, I think.)
--Larry Garfield
So you're saying that would generate a callable equivalent to:
$fnMethod = fn (Zok $zok, string $zebranky): Frungy => $zok->Pik($zebranky);
Yes.
(We're also now rather far afield from the OP's request, I think.)
Well, yeah. Deliberately so.
Starting a discussion with a solution prevents people discussing and
agreeing on what the actual problems to be solved are, and fixates on
a single, in this case 'not great', solution.
As I said, I think people should say clearly what the problem they are
trying to solve first, before suggesting solutions.
I'd probably push for something more compact as a syntax
Quite. Indeed. Yes.
Danack wrote:
Well, other than to use a different syntax.
$(Zoq, Pik);
cheers
Dan
Ack
You're absolutely correct, the problem I was trying to solve was the ability to reference a method without throwing around magic strings, apologies if that wasn't clear.
The whole idea, or rather, need/want for it came about because of time spent with Javas functional side, though I understand that's safer to do because of the way Java handles type safety.
The ::class pseudo type is great for providing a classes FQN, and class-string is great for ensuring values are valid class strings when using static analysis, but we don't have anything for methods, outside of IDE completion.
The solution wouldn't even need to generate a closure, TBH, it would generate something that could then be used in the same manner.
Using the current features of PHP, I'd probably imagine something that does just about the same as this:
$method = new ReflectionMethod(MyClass::class, 'method');
$collection->filter($method);
Where the filter method does:
foreach ($items as $item) {
if ($method->invoke($item)) {
//
}
}
Although reflection doesn't have the overhead that everyone thinks it does, this is a less than ideal solution, but I think it accurately portrays what I'm looking for.
Best Regards,
Ollie Read
You're absolutely correct, the problem I was trying to solve was the ability to reference a method without throwing around magic strings, apologies if that wasn't clear.
The whole idea, or rather, need/want for it came about because of time spent with Javas functional side, though I understand that's safer to do because of the way Java handles type safety.
The ::class pseudo type is great for providing a classes FQN, and class-string is great for ensuring values are valid class strings when using static analysis, but we don't have anything for methods, outside of IDE completion.
The solution wouldn't even need to generate a closure, TBH, it would generate something that could then be used in the same manner.
Using the current features of PHP, I'd probably imagine something that does just about the same as this:
$method = new ReflectionMethod(MyClass::class, 'method');
$collection->filter($method);
Where the filter method does:
foreach ($items as $item) {
if ($method->invoke($item)) {
//
}
}Although reflection doesn't have the overhead that everyone thinks it does, this is a less than ideal solution, but I think it accurately portrays what I'm looking for.
Best Regards,
Ollie Read
The ::class pseudo type is great for providing a classes FQN, and class-string is great for ensuring values are valid class strings when using static analysis, but we don't have anything for methods, outside of IDE completion.
The solution wouldn't even need to generate a closure, TBH, it would generate something that could then be used in the same manner.
In C#, they have the nameof()
operator that returns the name of the
symbol passed. So you still end up with a string, but the symbol can
easily be refactored.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof
Maybe something like this could help address the problem?
Robert Landers
Software Engineer
Utrecht NL