PHP 8: Method Overloading, The Ressurection

php.internals

David Rodrigues

7 years ago
Hello everybody! I know that method overloading was discussed on past and because a lot of reasons it was declined [1]. I think that it should be considered maybe for PHP 8, but for now I would like to suggest a different implementation. Because of a stronger support to parameter types that PHP has now, I think that is possible make this process more faster than before by handling it only in case of exceptions. I wil explain better. The biggest problem that I see about this topic is the loss of performance, but I think that it could be avoided by create a new keyword "overload" that should be applied before methods that could be overloaded. For instance: overload function sum(int $a, int $b): int; overload function sum(float $b, float $b): float; In this case, when call a method PHP could identify if it have a overloaded implementation, so ONLY in this case it will reduce performace to decided which implementation it should call. I don't know how PHP could treats it internally, but when overload keyword is identified, I think that it could generates a hash like "sum/int,int/int" and "sum/float,float/float" (method name, parameters types, return type). I don't know. So when method is called, it should convert the argument types and create a similar hash, making possible to identify what overloaded method should be run. It seems slow, but I think that the opcache could simplify the next operations to connect directly the call to the correct overloaded method. Please, tell what do you think, because I really wants method overload on PHP 8 or before. Currently we should do some code hacks to make it possible that certainly is slower than a native implementation. Thanks! [1] https://marc.info/?l=php-internals&m=121397217528280&w=2
-- David Rodrigues

Christoph Becker

7 years ago
On 03.02.2019 at 19:39, David Rodrigues wrote:
> overload function sum(int $a, int $b): int; > overload function sum(float $b, float $b): float;
Which function would sum(17.4, 42) call? Also consider: sum(PHP_INT_MAX, PHP_INT_MAX) vs. sum(PHP_INT_MAX+1, PHP_INT_MAX+1)
-- Christoph M. Becker

Rowan Collins

7 years ago
On 03/02/2019 19:00, Christoph M. Becker wrote:
> On 03.02.2019 at 19:39, David Rodrigues wrote: > >> overload function sum(int $a, int $b): int; >> overload function sum(float $b, float $b): float; > Which function would sum(17.4, 42) call? Also consider: > > sum(PHP_INT_MAX, PHP_INT_MAX) > > vs. > > sum(PHP_INT_MAX+1, PHP_INT_MAX+1)
Yes, marking overloaded functions explicitly definitely helps, but I think the dispatch part is more complex than it first seems. Classes and interfaces require a bit of subtlety too: class Foo {} class Bar extends Foo {} overload function foo(Foo $a, Foo $b); overload function foo(Foo $a, Bar $b); overload function foo(Bar $a, Foo $b); foo(new Bar, new Bar); There are plenty of cases more complex than this, e.g. when a class implements multiple interfaces, and you need to find a matching signature. That's not to say it's impossible, just don't underestimate the edge cases you'll need to legislate for. Oh, and note that return types can't participate in overloading, because there's no concept of "desired type" to choose between them. Regards,
-- Rowan Collins [IMSoP]

David Rodrigues

7 years ago
Em dom, 3 de fev de 2019 às 17:19, Rowan Collins <rowan.collins@gmail.com> escreveu:
> On 03/02/2019 19:00, Christoph M. Becker wrote: > > On 03.02.2019 at 19:39, David Rodrigues wrote: > > > >> overload function sum(int $a, int $b): int; > >> overload function sum(float $b, float $b): float; > > Which function would sum(17.4, 42) call? Also consider: > > > > sum(PHP_INT_MAX, PHP_INT_MAX) > > > > vs. > > > > sum(PHP_INT_MAX+1, PHP_INT_MAX+1) > > > Yes, marking overloaded functions explicitly definitely helps, but I > think the dispatch part is more complex than it first seems. Classes and > interfaces require a bit of subtlety too: > > class Foo {} > class Bar extends Foo {} > > overload function foo(Foo $a, Foo $b); > overload function foo(Foo $a, Bar $b); > overload function foo(Bar $a, Foo $b); > > foo(new Bar, new Bar); >
This example could be complex. So, what other languages does in cases like that? Java, for instance.
> > There are plenty of cases more complex than this, e.g. when a class > implements multiple interfaces, and you need to find a matching signature. > > That's not to say it's impossible, just don't underestimate the edge > cases you'll need to legislate for. > > Oh, and note that return types can't participate in overloading, because > there's no concept of "desired type" to choose between them. >
You are right, in general. But still is possible identify the desired type if return is used as argument to a typed parameter, but I still thinks that it could not be done in general terms, so maybe we should not includes return type to overloading.
> > Regards, > > -- > Rowan Collins > [IMSoP] > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- David Rodrigues

Marcos Passos

7 years ago
I believe we can adopt the behaviour of mature languages that supports this feature. Java, for instance, throw a compile-time error saying that the method signature is ambiguous. On Sun, Feb 3, 2019 at 17:51 David Rodrigues <david.proweb@gmail.com> wrote:

Cees-Jan Kiewiet

7 years ago
Well there should be a: overload function sum(float $b, float $b): float; in that case or throw an error for no matching function available imho. On Sun, Feb 3, 2019 at 8:00 PM Christoph M. Becker <cmbecker69@gmx.de> wrote:
> On 03.02.2019 at 19:39, David Rodrigues wrote: > > > overload function sum(int $a, int $b): int; > > overload function sum(float $b, float $b): float; > > Which function would sum(17.4, 42) call? Also consider: > > sum(PHP_INT_MAX, PHP_INT_MAX) > > vs. > > sum(PHP_INT_MAX+1, PHP_INT_MAX+1) > > -- > Christoph M. Becker > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- <http://wyrihaximus.net/> Website <http://wyrihaximus.net/?utm_source=signature&utm_medium=email&utm_campaign=emailsignature> | Blog <http://blog.wyrihaximus.net/?utm_source=signature&utm_medium=email&utm_campaign=emailsignature> | Github <https://github.com/WyriHaximus> | Linkedin <http://nl.linkedin.com/in/ceesjankiewiet> | Twitter <http://twitter.com/wyrihaximus> | Facebook <http://www.facebook.com/CeesJan.Kiewiet>

Dan Ackroyd

7 years ago
On Sun, 3 Feb 2019 at 18:39, David Rodrigues <david.proweb@gmail.com> wrote:
> > Hello everybody! > > The biggest problem that I see about this topic is the loss of performance,
Although performance impact might be a problem, my impression is that PHP has some problems supporting method overloading that don't exist in other languages.
> So, what other languages does in cases like > that? Java, for instance.
Java doesn't have type-juggling so they avoid quite a few problems with dynamic types. Marcos Passos wrote:
> Java, for instance, throw a compile-time error saying that the > method signature is ambiguous.
Java compiles all the code in a program before running it, which makes that solution possible. PHP doesn't and so has to make some decisions at run-time. As Christoph wrote:
> overload function sum(int $a, int $b): int; > overload function sum(float $b, float $b): float; > > > Which function would sum(17.4, 42) call?
That's the fundamental problem that would need addressing. I believe that to have a chance of passing, any method overloading RFC would need to have a really comprehensive answer that covers all of the edge cases. But in addition to that, a really strong argument for why method overloading is a good thing needs to be made as well, particularly as the equivalent behaviour can be done in userland*. btw It is possible, that the thing mostly likely to happen, and provide the most benefit for you, would be support in your IDE for autocompletion as implemented in userland. Which might take a lot less effort than implmenting the feature in PHP core... cheers Dan Ack * userland implementation: /** * @method sum(int $a, int $b): int; * @method sum(float $b, float $b): float; */ class Foo { public function __call($name, $arguments) { if ($name === 'sum' && is_int($arguments[0]) && is_int($arguments[1])) { return $this->sumInts($arguments[0], $arguments[1]); } if ($name === 'sum' && is_float($arguments[0]) && is_float($arguments[1])) { return $this->sumFloats($arguments[0], $arguments[1]); } throw new \InvalidArgumentException("Don't know how to call this"); } private function sumInts(int $a, int $b): int { // echo "sumInts was called.\n"; return $a + $b; } private function sumFloats(int $a, int $b): int { // echo "sumFloats was called.\n"; return $a + $b; } } $foo = new Foo(); $foo->sum(4, 4); $foo->sum(4.1, 4.1);