r/PHP•
helloworder

Operator Overloading RFC is in voting. What are your thoughts on this feature?

Personally I feel PHP needs this RFC.

It adds parity between the built-in features and userland features of the language, because we already have overloaded operations for some internal classes.

It also will surely make life easier for some math libraries, while other libraries that do not require this feature will not be affected at all.

Sure it might be misused just as any other feature of the language currently. Many modern programming languages have this feature and it turns out to be a very useful tool (Python, Rust, C#, C++ to name a few).

I also like the operator +() approach of this RFC instead of the magic method approach of the previous one.

The link to RFC (and the voting).

The link to the discussion mailing list thread.

The voting has just started and does not already look promising, but I wanted to know what the community thinks of it.

View Poll

Posted Jan 3, 2022, 12:51 PM UTC

4065
marktheprogrammer•

I voted yes, on here and on the RFC.

Mathematics is an area where the PHP ecosystem does not get much love compared to alternatives such as Python.

Mathematics / data modelling / ML are some of the fastest growing sectors in software development. They are also key entrypoints for people who are being introduced to software development and the larger programming language ecosystem for the first time.

This RFC provides tools to improve PHP's long-term appeal and competativeness in these fields.

That can only be a good thing.

29
Firehed•

While I agree with your perspective, is there something that leads you to believe PHP would be adopted for ML if the language had these additions?

All of the data scientists and data engineers I've worked with would refuse out of principle, and the less-technical ones (great at math, poor at coding) struggle to program in the tools they're already familiar with. All of the evidence I've encountered indicates that nothing short of a Thanos snap-like experience that vanishes Python and its related tooling from existence would lead to PHP being adopted in that space.

Let's also not forget about all of the Fortran-based underpinnings in the Python math libraries. Those would similarly need to be ported across for... many reasons.

While under very rare circumstances I would find operator overloading valuable, they're not what's holding back PHP from being adopted in the ML space.

7
JordanLeDoux•

RFC author here. I will be bringing many improvements to PHP in this space in general. PHP is better for this kind of application than Python as a language, it just doesn't have the language features to support it right now.

So I've made that my mission.

15
Firehed•

I won't debate you on how well or not PHP can serve this area of technology and computing (I think you're correct there, and in any case I'm biased in favor of PHP and strongly against Python).

But the "as a language" is doing a tremendous amount of work in that statement. There's no ML/DS ecosystem, no motivation to switch, and an army of relatively stubborn data scientists that will try to murder you if you want them to use anything other than Pandas. Code and language quality is very often not a concern for them. Being forced to add semicolons and write out function instead of def and use $ everywhere certainly won't sell them on anything, and the vastly better type system is an anti-feature for them if the code I've seen is anything to go by.

8
JordanLeDoux•

Well, I don't want to sound like I'm dismissing those things as unimportant, but what I'm focused on is the idea that those problems can't be solved unless the language actually supports the domain first. I'm trying to focus on the features themselves before looking at how adoption could be driven.

9
zmitic•

I absolutely love this RFC, especially with operator instead of magic method (as before).

​

I hope it will pass and have a question: would this RFC, or one in future, allow implicit interface implementation like how Stringable and BackedEnum work?

​

Simplified example; if class has:

​

class Account
{
    operator +(Money $money, OperandPosition $pos): int
}

​

it would implicitly implement Addable<Money, int> ?

1
dave8271•

I think PHP 8.1 and upwards in future has great potential, both in terms of speed and language features, for applications such as ML outside of web development, with a lower barrier of entry to get started in that field than almost any other language to boot.

I agree that convincing people outside the existing PHP ecosystem of this is in practice more difficult and operator overloading wouldn't help solve the problem.

But regardless, in a world where you do want to use PHP for this kind of problem solving, you don't need operator overloading to do it. Python does support overloading and I've seen plenty of ML-type libraries which don't make use of it. It's a feature which ultimately tends to lead to confusion and design mess, because operators suddenly have a multitude of different meanings which aren't transparent.

I'd much rather see PHP's type system further developed and the ability to overload function signatures. We can partially do that at the moment with spread or union types, but we can't neatly do:

function foo(int $x, A $y) { ... }

function foo(int $x, B $y) { ... }

which would be far more useful, imo.

7
moises-vortice•

And something to specify strict arrays like:

function foo(int $x, array[A|B] $y) { ... }

would be fine.

3
Girgias•

This is literally impossible and will never happen. It has been said countless times by core devs who know more about the engine. [1] And in particular Nikita.

So maybe it would be more useful, but this is an even harder barrier than generics.

2
dave8271•

What gives someone voting rights on RFCs, out of interest?

4
marktheprogrammer•

Usually contributions to php-src or the documentation.

7
dave8271•

What kind of contributions though? Loads of people have added something to php-src or the docs and AFAIK they don't all get to vote? Who decides?

4
DrWhatNoName•

Dedicated contributions

1
butthole_network•

I'm divided. Operator overloading can create some exceptionally elegant code, and the idea of adding two objects together makes perfect sense in so many cases I've come across in the past.

With that said, the potential for writing unintuitive crap is very high, and I'm worried on some fronts about how some of my peers may use this. With that said, I'm worried about how they use interfaces, and the world hasn't ended yet.

It's another tool in the box, it has the potential to significantly improve readability in some cases, and lots of other languages have it. Yes. I'll just need to work code reviews to understand what are good implementations, just like anything else :)

28
KFCConspiracy•

I've seen abuse of it before, and how it can make things way more confusing, but it's also a cool feature that I like when it's used with appropriate rules... It can really boost productivity and code clarity. I'm not married to it being a "NEED", but I think it might be nice to have, I think like many things it'll be up to teams to define sane rules for using it. I don't think abuse is necessarily a great argument for why a feature shouldn't exist. I just think the PHP community should be aware, but that's why most sane teams have code reviews and use linters.

11
zmitic•

I can't wait for it. The reason is that I use lots of lazy evaluations; old version of example is here.

So I could have a function like this:

​

function doSomething(int|LazyValue $param): int {
    return $param + 42;
}

​

but without operator overload:

​

function doSomething(int|LazyValue $param): int {
    if ($param instanceof LazyValue) {
        $param = $param->getValue();
    }

    return $param + 42;
}

​

Updated:

I would prefer for RFC to require interfaces, if possible:

​

interface Addable
{
    public function add(int|float|Addable $param): int|float|Addable
}

​

End result:

​

function doSomething(int|Addable $param): int {
    return $param + 42;
}

​

Interfaces would make easier job for static analysis, allow ctrl+click to find all Addable implementations and avoid some WTF situations in older apps with __add() method.

7
T_Butler•

I don't think an interface would be practical here because classes X and Y could both implement it but be incompatible for mathematical operations.

An int and gmp might be compatible but consider where we would want to represent something else like feet and inches, adding an int to a foot might not work.

$measurement1 = new ImperialUnit('5\'11');
$measurement2 = new ImperialUnit('6"');

$total = $measurement1 + $measurement2;
10
zmitic•

I don't think an interface would be practical here because classes X and Y could both implement it but be incompatible for mathematical operations.

​

Agreed, but if we had generics; wouldn't it be possible? Or if never parameter type was implemented, then we could dynamically change the signature; correct?

4
ToBe27•

But wouldnt it be much better to have two identically named functions with different parameters? Otherwise you would still have to check what you actually got inside your one function. So, wouldnt it be better to have propper overloaded functions as a whole instead of just specifying multiple allowed types?
Sorry if I misread the RFC...

3
zmitic•

But wouldnt it be much better to have two identically named functions with different parameters

​

Honestly, I would really hate that. I did work with Angular and Typescript which had lots of overloaded methods and at least for me, it was a nightmare.

​

The big problem was that ctrl+click would show me all those methods which I would have to read first, and then pick which one I want. At least with unions, everything is one place.

​

Personal experience only, maybe someone prefers them but I don't.

1
Danack•

I would prefer for RFC to require interfaces, if possible:

Did read the reasons why that isn't appropriate in the RFC ?

https://wiki.php.net/rfc/user_defined_operator_overloads#why_not_interfaces

2
zmitic•

I did, but I think it is mostly because of lack of generics.

For example:

​

interface Addable<T>
{
    public function add(T $param): T
}

class Money implements Addable<int|Money>{}

​

But I could be wrong, that's why I said if possible; maybe other contributors can come up with some idea.

1
Danack•

That doesn't help anything. If you're using an interface as a type e.g.

function foo (Addable $x, int $y)
{
    return $x + $y;
}

You can't tell if the code is valid or not from the interface.

3
przemo_li•

You can fix it even today. Introduce StrictValue (as opposed to LazyVal), and rewrite that method into:

function doSomething(StrictValue|LazyValue $param): int {
    return $param.getValue() + 42;
}

or even with repacking:

function doSomething(StrictValue|LazyValue $param): StrictValue {
    return StrictValue($param.getValue() + 42);
}

PHPStan/Psalm generics will help you with reclaiming type information.

Bonus points for enabling returning LazyValue instead of StrictValue while retaining interface.

-1
zmitic•

It is not the same thing. Imagine if doSomething only accepted integers, but you want to refactor it to support laziness as well.

​

It is easy to just union it with LazyValue; no other changes inside the method would be needed.

The example is too simple, you should imagine cascading math operations. Something like chain of promises.

1
przemo_li•

Rewrite would still be local to call sites. every doSomething(4) becomes doSomething(new StrictValue(4)).

This simplifies internal structure of doSomething and implements full set of possibilities. Look how LazyValue and StrictValue complement each other. This suggest deep connection.

Finally, StrictValue could support any other interface LazyValue does, even those methods that aren't operators.

1
zmitic•

No, I don't want to fix all call sites, they are free to send integers as before. I only want to expand doSomething to be more flexible, the rest of code stays the same.

​

So if there were 10 places that send integer to doSomething, and I want 11th and 12th to work with lazy values... it would be easy to do. Those 10 places can stay as-is.

Anyway, the example is just too simple and not realistic. In reality, cascading math operations would mostly benefit from this; single function doesn't matter.

2
Just_Maintenance•

My first language was C# and I have loved operator overloading ever since. I would love to have it again.

6
systematical•

There have been times where I would have found this useful. I can think of features I'd put ahead of it, namely C# get/set and Java Lombok style functionality.

4
jets-fool•

Even without understanding what the above code does (it's an excerpt from a Coppersmith attack on RSA), it should be obvious that the second code is a lot clearer.

Yeah...not really.

4
dave8271•

It's a contradiction the RFC says what you quoted about the example using * operator everywhere, but then goes on to say "Using * will mean 'multiplication' in many contexts, but there are domains such as linear algebra where this may not be a 'mul' operation at all."

Right, so it's not clearer, is it? Because * could mean anything.

This is my concern...operator overloading is one of those things that feels really neat when you use it yourself, for code you wrote and you understand, but just becomes a confusing mess the second you're having to deal with operator semantics someone else wrote and understood differently.

2
anurat-•

It's losing 5 to 9 man.

2
[deleted]•

[deleted]

1
atimm•

12 to 10 is still a loss. It needs a 2/3 majority to pass.

9
dave8271•

I'd vote no. In principle I like the idea of operator overloading, but it's one of those features which is cool if you're just imagining the new syntax available to you as an end-user, but may have other reasons to be impractical or inadvisable in the language and implementation.

One of the things which is good about not having user-defined operators or overloads is when you see an operator, you know exactly what it's doing, immediately, and so does your static analyzer. You know there isn't a bunch of complexity swept under the rug.

Operator overloading is also very easy to abuse, even by accident or with good intentions. The RFC opts for e.g. operator +() as the syntax, specifically noting that this is preferred over something like function __add() because the + operator might not be used in this case for addition. But addition or unionization is exactly what the + operator means. If you're using it to do something different or more complex [than something semantically equivalent to addition on two objects which can't natively be added together], it's better and more readable code to just use a regular function.

Is the syntactical convenience worth this ambiguity? Imagine we have a class representing a complex number. Is $complexNumberA += $complexNumberB really that much of a saving over $complexNumberA->add($complexNumberB)? Yes it's marginally shorter and prettier, provided you know that the + operator is still being used for addition. But it's hardly such a saving as to be worth the complexity it adds to the language.

2†
JordanLeDoux•

But it's hardly such a saving as to be worth the complexity it adds to the language.

I would argue that it adds no real complexity, just imagined complexity.

$objectA + $objectB results in a fatal error right now. Your application simply stops. After this RFC, it still results in a fatal error.

If you ever see any object used with operators, it must use operator overloads. There's no ambiguity. Any object used with operators will result in a fatal error unless there is an overload involved. If you use it with the wrong type it will still result in a fatal error.

The feedback for developers, even without static analysis, will be rapid and to the point.

So if that concern is dealt with, it essentially boils down to some form of "the feature will be abused" since the "complexity" would then be entirely in the form of "the implementation is doing unexpected things". But this is fundamentally incorrect if we look at very nearly every language ever made that has this feature.

This is a very convincing boogeyman, but after the months of research I did for this RFC, I think it's just that: a boogeyman.

Every single maintainer of any math library or currency library which I have talked to about this feature has told me some version of "the lack of this feature is painful and hurts my library and my development". It is not "nice", it is something that library maintainers are currently papering over with hack upon hack to make it work.

If this feature is simply sugar, why does every single extension that deals with this space implement their own operator overloads? Why does GMP have it? Why does ext-decimal have it?

I understand your objections, but I do not agree with them.

8
dave8271•

Great reply, thank you for taking the time and all I can say in response is likewise, I fully understand why you think this is a very valuable feature to add, indeed I've used overloading in Python myself, I just don't think it's quite right for PHP (yet), or at least I'm not yet convinced enough that I'd hypothetically vote for it today.

1
Disgruntled__Goat•

What is the issue with static analyzers? A SA would know what the operator is doing as it knows the type of the variables and can see if that operator is defined on the class.

2
dave8271•

It's not necessarily an issue, a static analyzer can be made to work with it, but it makes the job more complex.

For example:

$result = $typeA + $typeB;

Is this A::operator+(B $b) or is it B::operator+(A $a)? What if you need to support multiple types of operand in either implementation? You're using an arbitrary length list of unions, or dynamic typing, so that's harder for the static analysis (or you as a developer) too. Much harder than $result = $typeA->addFromTypeB($b);

1
Disgruntled__Goat•

Is this A::operator+(B $b) or is it B::operator+(A $a)?

Why would it not be the first one? Isn't it just done in order same as normal numbers?

2
dave8271•

Not if only one of the two is defined. Again, I'm not saying a static analyzer can't do it, I'm saying the complexity of the language and thus the work such a tool has to do and be programmed to do is increased.

0
Danack•

Is this A::operator+(B $b) or is it B::operator+(A $a)?

That's a trivial problem for static analyzers to figure out...

Much harder than $result = $typeA->addFromTypeB($b);

Yes. Now compare non-trivial examples:

// with operator overloading
$result = (
    $c0 * $ms0 * gmp_invert($ms0, $n0)
  + $c1 * $ms1 * gmp_invert($ms1, $n1)
  + $c2 * $ms2 * gmp_invert($ms2, $n2)
) % ($n0 * $n1 * $n2);

vs

// without operator overloading
$result = gmp_mod(
    gmp_add(
        gmp_mul($c0, gmp_mul($ms0, gmp_invert($ms0, $n0))),
        gmp_add(
            gmp_mul($c1, gmp_mul($ms1, gmp_invert($ms1, $n1))),
            gmp_mul($c2, gmp_mul($ms2, gmp_invert($ms2, $n2)))
        )
    ),
    gmp_mul($n0, gmp_mul($n1, $n2))
);
2
dave8271•

That's a trivial problem for static analyzers to figure out...

It isn't. It's way, way harder to build a parser to interpret $a + $b when + can mean anything and a lot more computation work has to go in to doing it. This is what I mean when I say people here are thinking about it only from the point of view of being a PHP user.

Yes. Now compare non-trivial examples:

These examples actually are trivial for an analyzer, because they can only be interpreted one straightforward way.

Now on the human side, the GMP example you gave is (marginally) easier to read with operator overloading, yes. It's actually still quite complex to read because of the nesting and figuring out precedence in the human eye, but it's slightly easier than the more verbose function names. But only because you know the operators do what they say on the tin. If it was possible that $c1 * $c2 could mean something completely different to $c1 * $c3 or $c3 * $c4 suddenly it's even harder to make any sense of than the more verbose alternative of function names.

0
[deleted]•

[deleted]

1
[deleted]•

[deleted]

1
sinnerou•

Just to clarify he voted no based on the new syntax added in this rfc, not against overloading as a concept.

1
marabutt•

Personally, I can't say I would have much of a use case but can see it could be useful. For me $a->add($b) is just as clear as $a+$b. If + was defined on a type, would array_sum($a,$b) be available for the type?

1
umulmrum•

It would be great if some of the no-voters could share their reasoning. There are some reasons given on the mailing list, but still I'd be interested if the voters reject the whole idea, the implementation, miss some work on edge-cases or other things.

1
Rikudou_Sage•

By the way, it's already possible using FFI and z-engine. I have a toy project using this: rikudou/units.

1
SavishSalacious•

Please tell me this leads us to Method and Constructor overloading like C# and Java. That would go a long way!

1
yehiaserag•

Didn't this get rejected in an old rfc??? I'm totally pro this, hope it passes

1
r448191•

One look at something like Django will show you this is a horrible, horrible idea.

0†
[deleted]•

[deleted]

-2†
algerd_by•

No. It will be harder to debug. Harder to read code

-3
przemo_li•

Inclusion of == and === operators is troubling. Those operators already have well defined meaning for objects. Therefore developers would be able to break any code that is already relaying on assumptions guaranteed by those operators.

-4
Drarok•

You clearly didn’t read it, then. They explicitly state the identity operator is excluded.

Why can't the identity operator be overloaded?

The identity operator === is used to check whether two variables contain the same object, or whether two non-objects have the same type and value.

The position of this RFC is that allowing the identity operator to be overloaded isn't a useful thing to do, as it wouldn't enable any new functionality but could introduce potentially terrible bugs in PHP programs where it is used.

11
Firehed•

TBH the identity operator is one of the things that I'd find useful to overload, specifically in the context of DTO comparison. But that's more because we don't have structs, and arrays often lack sufficient structure.

That said, I'd probably vote against its inclusion, as the likelihood of misuse greatly exceeds the utility of having it.

3
butthole_network•

I think overloading == will solve that use case for you, as it becomes a "soft" comparison using whatever comparison logic the type implements. === remains sacrosanct, and can always be relied on for simpler use cases.

3
Firehed•

It could, but that a) necessitates adjustments to all sorts of linting rules that ban ==, and b) has a similarly-confusing implication of == comparison of the internal properties even if they're compared with ===.

I don't think there's a clear win either way; the root issue is there's no by-value structure other than arrays, and arrays are a relatively poor fit for many things.

3