Hello internals,
I'd like to introduce a new RFC: https://wiki.php.net/rfc/operator_overrides_lite which extends the GMP extension to support a limited set of operator overriding to developers. It's designed to be limited and relatively simple, while still being quite powerful. It would only be available if you have the GMP extension installed and enabled, but would allow for creating powerful unit libraries (such as representing money, duration, etc).
I'm very interested in your feedback!
This was initiated from a discussion in another thread: https://externals.io/message/123872
Thanks for your time,
Rob Landers
Hello internals,
I'd like to introduce a new RFC:
https://wiki.php.net/rfc/operator_overrides_lite which extends the GMP
extension to support a limited set of operator overriding to developers.
It's designed to be limited and relatively simple, while still being quite
powerful. It would only be available if you have the GMP extension
installed and enabled, but would allow for creating powerful unit libraries
(such as representing money, duration, etc).I'm very interested in your feedback!
This was initiated from a discussion in another thread:
https://externals.io/message/123872Thanks for your time,
Rob Landers
You probably have not actually looked at implementing this yet, so let me
give you some advice:
- You probably are not yet aware that operands get reordered, or their
ordering is not guaranteed, when a comparison occurs. This is because there
is no op code (currently) for Greater Than or Greater Than Or Equal.
Instead the comparison is reordered so that the operands are swapped and a
Less Than or a Less Than Or Equal is performed. This is perfectly
acceptable until you need to determine whether the left or right operand's
function needs to be called, such as with objects. - The fact that the signature uses
mixed
for the typing removes a lot of
the safety features the previous RFC had, and makes a lot of the concerns
that stopped that RFC worse. In PHP currently, an object used with any
non-comparison operand results in a TypeError. In the previous proposal,
this behavior was preserved even for objects which implement overloads
unless they specifically listed the type as accepted in the overload
definition. What this did is ensure that anything OTHER than a TypeError
guaranteed the developer that they were dealing with an operator overload
that they could go inspect. - The private/protected distinction is fairly meaningless for the
functions that implement overloads, because the privacy of the function is
ignored completely when it is executed to evaluate an operator. - The
static
distinction is also fairly meaningless, as in PHP there is
no situation possible where an operator overload can occur WITHOUT it
operating on objects themselves. - Your voting choices actually constitute something that is not allowed
for RFCs. A 'yes' vote allows operator overloads for GMP, a 'no' vote
changes GMP tofinal
. There is no option here to leave PHP as it
currently is, which is what a 'no' vote should mean. - The
comparable
function you propose doesn't actually have an operator
it corresponds to. There is no operator in PHP for "is the left value
comparable with the right value". There are operators for comparisons
themselves, which I assume you meant, but a bool is insufficient as a
return type for that.
There's probably more, but this is a start for you to make some
improvements and changes. However, I had to warn you before you put in too
much effort that I am fairly certain this RFC has very nearly zero chance
of passing.
Jordan
__
Hello internals,I'd like to introduce a new RFC: https://wiki.php.net/rfc/operator_overrides_lite which extends the GMP extension to support a limited set of operator overriding to developers. It's designed to be limited and relatively simple, while still being quite powerful. It would only be available if you have the GMP extension installed and enabled, but would allow for creating powerful unit libraries (such as representing money, duration, etc).
I'm very interested in your feedback!
This was initiated from a discussion in another thread: https://externals.io/message/123872
Thanks for your time,
Rob Landers
You probably have not actually looked at implementing this yet, so let me give you some advice:
- You probably are not yet aware that operands get reordered, or their ordering is not guaranteed, when a comparison occurs. This is because there is no op code (currently) for Greater Than or Greater Than Or Equal. Instead the comparison is reordered so that the operands are swapped and a Less Than or a Less Than Or Equal is performed. This is perfectly acceptable until you need to determine whether the left or right operand's function needs to be called, such as with objects.
I actually did know that from implementing something like this for a client. That is why I chose “comparable” to let the developer decide if two objects are comparable or not. For example, money probably can’t be equal, less than, or more than a plain number, distance, or time. All objects must be comparable for the comparison to succeed. I have updated the RFC.
- The fact that the signature uses
mixed
for the typing removes a lot of the safety features the previous RFC had, and makes a lot of the concerns that stopped that RFC worse. In PHP currently, an object used with any non-comparison operand results in a TypeError. In the previous proposal, this behavior was preserved even for objects which implement overloads unless they specifically listed the type as accepted in the overload definition. What this did is ensure that anything OTHER than a TypeError guaranteed the developer that they were dealing with an operator overload that they could go inspect.
The mixed type is on purpose until I have an implementation. I haven’t decided if both will always be a \GMP object or if float|int|string might get thrown in there too. I want the former, but I’ll have to see what I can do.
I’ll have to look into that exception handling, but I don’t want to break the current behavior of GMP or make any big changes to support this.
- The private/protected distinction is fairly meaningless for the functions that implement overloads, because the privacy of the function is ignored completely when it is executed to evaluate an operator.
Hmm. I like the idea of protected, because it gives a structure to it that is apparent and usable right from the IDE. You just “fill in the blanks” or stick with the default behavior.
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.
For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
- Your voting choices actually constitute something that is not allowed for RFCs. A 'yes' vote allows operator overloads for GMP, a 'no' vote changes GMP to
final
. There is no option here to leave PHP as it currently is, which is what a 'no' vote should mean.
Yeah. I was actually wondering about this… I believe Gina made it kinda clear that it was accidentally non-final. So I was just saving everyone an RFC — maybe I will make it a secondary vote for the case where the RFC is declined. I’ve now updated the RFC.
- The
comparable
function you propose doesn't actually have an operator it corresponds to. There is no operator in PHP for "is the left value comparable with the right value". There are operators for comparisons themselves, which I assume you meant, but a bool is insufficient as a return type for that.
In the engine, there’s just a compare function for internal overrides. So we just check that everyone agrees that the two objects are comparable and then pass it on to “business as usual.”
There's probably more, but this is a start for you to make some improvements and changes. However, I had to warn you before you put in too much effort that I am fairly certain this RFC has very nearly zero chance of passing.
Jordan
I have absolutely zero faith this will pass, but I will give it a fair shot. It’s the least I can do.
— Rob
- The private/protected distinction is fairly meaningless for the
functions that implement overloads, because the privacy of the function is
ignored completely when it is executed to evaluate an operator.Hmm. I like the idea of protected, because it gives a structure to it that
is apparent and usable right from the IDE. You just “fill in the blanks” or
stick with the default behavior.
I do not understand how the visibility has any impact on the usability you
are seeking to provide.
- The
static
distinction is also fairly meaningless, as in PHP there is
no situation possible where an operator overload can occur WITHOUT it
operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type,
not the instance. The object instances may not even know their value, they
merely represent the value.
A GMP object instance that does not know its value? What are you even
talking about? Can you show me some code explaining what you mean? I had
literally months of this argument for the operator overloads RFC, and
studied the overload implementations in six other languages as part of
writing that RFC, I feel like I understand this topic fairly well. But I do
not understand what you are saying here.
- The
comparable
function you propose doesn't actually have an operator
it corresponds to. There is no operator in PHP for "is the left value
comparable with the right value". There are operators for comparisons
themselves, which I assume you meant, but a bool is insufficient as a
return type for that.In the engine, there’s just a compare function for internal overrides. So
we just check that everyone agrees that the two objects are comparable and
then pass it on to “business as usual.”
I'm aware of how the compare handler for class entries and zend_compare
interact. What I am saying is that your design is insufficient for <=>. You
cannot return false
from this method to mean uncomparable, and true
to
mean comparable. The zend_compare function can validly return 0 or -1, with
the -1 being used for both less than OR greater than because the operands
are reordered to always be a less than comparison. Then 1, which normally
is used for greater than, is used to mean uncomparable.
If you alter GMP so that the compare handler directly calls the class entry
for this "comparable" function, you will be mixing multiple return semantic
meanings, assuming you can construct a way to make the values normalize. If
you implemented "comparable" in the way you are describing, $obj1 == $obj2
would ALWAYS be true, no matter what their values are, completely changing
the meaning of the equality operator for GMP.
As you are proposing this without any genuine expectation you can pass it,
this will be the last energy I will invest into helping.
Jordan
__
- The private/protected distinction is fairly meaningless for the functions that implement overloads, because the privacy of the function is ignored completely when it is executed to evaluate an operator.
Hmm. I like the idea of protected, because it gives a structure to it that is apparent and usable right from the IDE. You just “fill in the blanks” or stick with the default behavior.
I do not understand how the visibility has any impact on the usability you are seeking to provide.
I guess it depends on what you mean by usability. From a technical standpoint, it has zero usability, but from a dev-ex standpoint, it has a huge amount of usability.
If we add these as protected methods to the base class, I merely need to write "protected static function<tab>" in my IDE and I will see all the methods I can write. It also lays bare "how it works" for a PHP developer without any magic, making it easier to document.
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
Heh, yeah, it's kinda weird. Let me explain. The GMP class hides its value in a "private" member (because the value isn't actually a number, but a GMP resource), so unless the programmer also sets the value to something they have access to, they won't know the value (but they can always cast $this to a number or operate on it directly). The only way they could get the value is to cast $this to float, which may lose some precision. The idea here is to "write the rules" where the value doesn't matter, or if it does, embed that as part of the rules.
For example, imagine we want to create a Field class, that takes a range for the field and keeps the value in the field. It might look something like this:
class IntField {
public function __construct(private int $max, int $value) {
parent::construct($value, 10);
}
public function add($left, $right): self {
// todo: guard that left can be added to right -- ie, both are integers
$result = parent::add($left, $right);
if ($result >= $this->max) return new IntField($this->max, $result % $this->max);
return new IntField($this->max, $result);
}
// todo: remaining implementation
}
I actually had a bit of a long-thought about it, and I think this is simpler (both to implement and to use) than the traditional approach, and more powerful. With the more traditional approach, how do define communitive rules? You are bound by traditional mathematics, more-or-less. From working in cryptography, a long time ago now, I can say that there are non-communitive rings where having access to both "left" and "right" can allow you to handle this quite well.
- The
comparable
function you propose doesn't actually have an operator it corresponds to. There is no operator in PHP for "is the left value comparable with the right value". There are operators for comparisons themselves, which I assume you meant, but a bool is insufficient as a return type for that.In the engine, there’s just a compare function for internal overrides. So we just check that everyone agrees that the two objects are comparable and then pass it on to “business as usual.”
I'm aware of how the compare handler for class entries and zend_compare interact. What I am saying is that your design is insufficient for <=>. You cannot return
false
from this method to mean uncomparable, andtrue
to mean comparable. The zend_compare function can validly return 0 or -1, with the -1 being used for both less than OR greater than because the operands are reordered to always be a less than comparison. Then 1, which normally is used for greater than, is used to mean uncomparable.
Ah, I mean that it calls this as a guard, before ever doing a comparison, not that this output will be used for comparison itself. This is deliberate, to keep it simple. If I get feedback that comparison should be implemented vs. a guard for comparison, I'd be happy to add it.
As you are proposing this without any genuine expectation you can pass it, this will be the last energy I will invest into helping.
I didn't mean it how I think you are taking it. To expand a bit on what I meant, "we" (as in people who want this feature, like myself) can only create RFCs for it. Maybe one day, the voters will change their mind, "we" will find an implementation they agree with, or they'll forget to vote "no" while enough people vote "yes." So, yes, I genuinely want this feature and I want to propose a feature that works and is the best I can come up with; at the same time, I don't expect it to pass, but I do hope that negative feedback will drive the feature to a compromise or solution that works. The only way to get there is by failing.
— Rob
__
- The private/protected distinction is fairly meaningless for the functions that implement overloads, because the privacy of the function is ignored completely when it is executed to evaluate an operator.
Hmm. I like the idea of protected, because it gives a structure to it that is apparent and usable right from the IDE. You just “fill in the blanks” or stick with the default behavior.
I do not understand how the visibility has any impact on the usability you are seeking to provide.
I guess it depends on what you mean by usability. From a technical standpoint, it has zero usability, but from a dev-ex standpoint, it has a huge amount of usability.
If we add these as protected methods to the base class, I merely need to write "protected static function<tab>" in my IDE and I will see all the methods I can write. It also lays bare "how it works" for a PHP developer without any magic, making it easier to document.
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
Heh, yeah, it's kinda weird. Let me explain. The GMP class hides its value in a "private" member (because the value isn't actually a number, but a GMP resource), so unless the programmer also sets the value to something they have access to, they won't know the value (but they can always cast $this to a number or operate on it directly). The only way they could get the value is to cast $this to float, which may lose some precision. The idea here is to "write the rules" where the value doesn't matter, or if it does, embed that as part of the rules.
For example, imagine we want to create a Field class, that takes a range for the field and keeps the value in the field. It might look something like this:
sigh: not enough coffee again and I saw the blunder as soon as I sent it. Here's the more correct implementation.
class IntField {
public function __construct(private int $max, int $value) {
parent::construct($value, 10);
}
protected static function add($left, $right): self {
// todo: guard that left can be added to right -- ie, both are integers
$result = parent::add($left, $right);
if ($result >= $left->max) return new IntField($left->max, $result % $left->max);
return new IntField($left->max, $result);
}
// todo: remaining implementation
}
I actually had a bit of a long-thought about it, and I think this is simpler (both to implement and to use) than the traditional approach, and more powerful. With the more traditional approach, how do define communitive rules? You are bound by traditional mathematics, more-or-less. From working in cryptography, a long time ago now, I can say that there are non-communitive rings where having access to both "left" and "right" can allow you to handle this quite well.
- The
comparable
function you propose doesn't actually have an operator it corresponds to. There is no operator in PHP for "is the left value comparable with the right value". There are operators for comparisons themselves, which I assume you meant, but a bool is insufficient as a return type for that.In the engine, there’s just a compare function for internal overrides. So we just check that everyone agrees that the two objects are comparable and then pass it on to “business as usual.”
I'm aware of how the compare handler for class entries and zend_compare interact. What I am saying is that your design is insufficient for <=>. You cannot return
false
from this method to mean uncomparable, andtrue
to mean comparable. The zend_compare function can validly return 0 or -1, with the -1 being used for both less than OR greater than because the operands are reordered to always be a less than comparison. Then 1, which normally is used for greater than, is used to mean uncomparable.Ah, I mean that it calls this as a guard, before ever doing a comparison, not that this output will be used for comparison itself. This is deliberate, to keep it simple. If I get feedback that comparison should be implemented vs. a guard for comparison, I'd be happy to add it.
As you are proposing this without any genuine expectation you can pass it, this will be the last energy I will invest into helping.
I didn't mean it how I think you are taking it. To expand a bit on what I meant, "we" (as in people who want this feature, like myself) can only create RFCs for it. Maybe one day, the voters will change their mind, "we" will find an implementation they agree with, or they'll forget to vote "no" while enough people vote "yes." So, yes, I genuinely want this feature and I want to propose a feature that works and is the best I can come up with; at the same time, I don't expect it to pass, but I do hope that negative feedback will drive the feature to a compromise or solution that works. The only way to get there is by failing.
— Rob
— Rob
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
A few minutes ago, I sent an email where I accidentally made the code non-static, and I think I see the merit in what you are saying. It felt so natural to use $this that I didn't even realize I was doing it wrong.
So, looking at your RFC and mine, I think this can be improved.
What would you suggest it look like and then we can work backwards from there?
Hi,
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
A few minutes ago, I sent an email where I accidentally made the code non-static, and I think I see the merit in what you are saying. It felt so natural to use $this that I didn't even realize I was doing it wrong.
So, looking at your RFC and mine, I think this can be improved.
What would you suggest it look like and then we can work backwards from there?
Here are my thoughts on your code.
In theory, inheriting from this "improved GMP class" would allow overloading of computational operators.
In effect, this acts like a "calcable interface", with the constructor passing in meaningless values to the parent constructor and the add method allowing the user to freely modify the return value.
This means that virtually any userland class can use the operator overloading feature via a "hack".
This approach is completely wrong. Rather than proposing this as is, it would be more constructive to propose support for operator overloading.
Regards,
Saki
Hi,
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
A few minutes ago, I sent an email where I accidentally made the code non-static, and I think I see the merit in what you are saying. It felt so natural to use $this that I didn't even realize I was doing it wrong.
So, looking at your RFC and mine, I think this can be improved.
What would you suggest it look like and then we can work backwards from there?
Here are my thoughts on your code.
In theory, inheriting from this "improved GMP class" would allow overloading of computational operators.
In effect, this acts like a "calcable interface", with the constructor passing in meaningless values to the parent constructor and the add method allowing the user to freely modify the return value.
This means that virtually any userland class can use the operator overloading feature via a "hack".
That is a very valid point, and I feel like it is something I definitely would have thought about since I love abusing features to do things they shouldn't. My hope was that by removing the ability to directly compare, it would reduce the usefulness of "hacking it" into generic overloading since you have to return a GMP instance ... but then, I guess, that GMP instance technically doesn't have to represent a number (though the rest of the engine will very much treat it as a number).
I will think on this some more...
For example, while eating lunch, I was considering whether this even needs to have anything to do with the GMP instance. I was only focusing on the GMP class because right now, it is non-final. Then I started thinking about Jordan's original proposal and how it could be simplified ... there's certainly things to think about.
This approach is completely wrong.
Ouch, I would hope it would have something useful to it. :)
Rather than proposing this as is, it would be more constructive to propose support for operator overloading.
That's been tried before, and this was an attempt at the far other extreme, "barely operator overloading". So, there is surely something in the middle. Hopefully.
— Rob
Hi,
Here are my thoughts on your code.
In theory, inheriting from this "improved GMP class" would allow overloading of computational operators.
In effect, this acts like a "calcable interface", with the constructor passing in meaningless values to the parent constructor and the add method allowing the user to freely modify the return value.
This means that virtually any userland class can use the operator overloading feature via a "hack".
That is a very valid point, and I feel like it is something I definitely would have thought about since I love abusing features to do things they shouldn't. My hope was that by removing the ability to directly compare, it would reduce the usefulness of "hacking it" into generic overloading since you have to return a GMP instance ... but then, I guess, that GMP instance technically doesn't have to represent a number (though the rest of the engine will very much treat it as a number).
I will think on this some more...
For example, while eating lunch, I was considering whether this even needs to have anything to do with the GMP instance. I was only focusing on the GMP class because right now, it is non-final. Then I started thinking about Jordan's original proposal and how it could be simplified ... there's certainly things to think about.
This approach is completely wrong.
Ouch, I would hope it would have something useful to it. :)
Rather than proposing this as is, it would be more constructive to propose support for operator overloading.
That's been tried before, and this was an attempt at the far other extreme, "barely operator overloading". So, there is surely something in the middle. Hopefully.
— Rob
I would like to state my opinion on this matter, making it clear that I am of the opinion that "GMP class should be final."
First of all, to meet the requirements that are the basis of this discussion, it is not actually necessary to expose the calculation logic; it is enough to simply specify the class of result.
A practical approach to get around this issue is to tell php what class the result should be.
For example, could prepare a method like resultClass(string $num1Class, string $num2Class, string $calcType): string|false
, and return the class name of the result based on the class names of the two objects to be calculated and the calculation type, such as addition or subtraction.
PHP calls this method as a "hook" when it finishes a calculation and returns the result to determine the class of the return value.
But I don't like this because it's a "hacky" way of doing things with zend. Also, I am concerned about the cost of doing this for every calculation.
The reason I'm putting together what I consider to be a bad method is because it's possible that you or someone else will take my idea, add a twist that I haven't thought of, and come up with a way to make it all work.
Regards,
Saki
I'll add a little more.
I would like to state my opinion on this matter, making it clear that I am of the opinion that "GMP class should be final."
First of all, to meet the requirements that are the basis of this discussion, it is not actually necessary to expose the calculation logic; it is enough to simply specify the class of result.
A practical approach to get around this issue is to tell php what class the result should be.
For example, could prepare a method like
resultClass(string $num1Class, string $num2Class, string $calcType): string|false
, and return the class name of the result based on the class names of the two objects to be calculated and the calculation type, such as addition or subtraction.PHP calls this method as a "hook" when it finishes a calculation and returns the result to determine the class of the return value.
But I don't like this because it's a "hacky" way of doing things with zend. Also, I am concerned about the cost of doing this for every calculation.
The reason I'm putting together what I consider to be a bad method is because it's possible that you or someone else will take my idea, add a twist that I haven't thought of, and come up with a way to make it all work.
Regards,
Saki
This method is actually not as simple as it may seem. The problem of commutativity still exists as to which to prioritize when calculating between different classes.
And if the child class has its own properties that must be specified in the constructor, you need to figure out how to store them.
Regards,
Saki
Hi,
Here are my thoughts on your code.
In theory, inheriting from this "improved GMP class" would allow overloading of computational operators.
In effect, this acts like a "calcable interface", with the constructor passing in meaningless values to the parent constructor and the add method allowing the user to freely modify the return value.
This means that virtually any userland class can use the operator overloading feature via a "hack".
That is a very valid point, and I feel like it is something I definitely would have thought about since I love abusing features to do things they shouldn't. My hope was that by removing the ability to directly compare, it would reduce the usefulness of "hacking it" into generic overloading since you have to return a GMP instance ... but then, I guess, that GMP instance technically doesn't have to represent a number (though the rest of the engine will very much treat it as a number).
I will think on this some more...
For example, while eating lunch, I was considering whether this even needs to have anything to do with the GMP instance. I was only focusing on the GMP class because right now, it is non-final. Then I started thinking about Jordan's original proposal and how it could be simplified ... there's certainly things to think about.
This approach is completely wrong.
Ouch, I would hope it would have something useful to it. :)
Rather than proposing this as is, it would be more constructive to propose support for operator overloading.
That's been tried before, and this was an attempt at the far other extreme, "barely operator overloading". So, there is surely something in the middle. Hopefully.
— Rob
I would like to state my opinion on this matter, making it clear that I am of the opinion that "GMP class should be final."
Yes, the more I consider it, the name of the base class will probably change, though GMP is a good candidate as a base class for "overridable numbers" for multiple reasons:
- near infinite numeric resolution
- arbitrarily big numbers (At least I've used it to perform operations on 256bit+ numbers)
- pretty fast
This gives a lot of power to whatever number is stored there and isn't limited to float or integer.
Secondly, numbers are pretty much the only thing you can't override in PHP. There's ArrayAccess if you want custom array-like things and there is Stringable if you want to control how your object is concatenated. There's no way to say "this number is a duration and you multiplying duration times how many cows you have is probably nonsensical." while also still allowing you to cast to an int/float if you want to do it anyway. Sure, you can write out $num->times($otherNum), but that isn't scannable or idiomatic to numbers, in general. What you (or at least I) want to do is write $num * $otherNum, especially because working out the order of operations is easy.
Before we switched to GMP on a project, working out $num->mul($other->minus($v->pow(2)))) was really hard to follow. With GMP it turned into $num * ($other - ($v ** 2)). Where it got really hard were things like $num * $other - $v * 3. Looking at this, you already know the order of operations, you know that $num * other, then $v * 3 comes first. When writing it out, you have to specifically state the order of operations: $num->mul($other)->sub($v->mul(3))).
It's really easy to mess that up.
First of all, to meet the requirements that are the basis of this discussion, it is not actually necessary to expose the calculation logic; it is enough to simply specify the class of result.
A practical approach to get around this issue is to tell php what class the result should be.
For example, could prepare a method like
resultClass(string $num1Class, string $num2Class, string $calcType): string|false
, and return the class name of the result based on the class names of the two objects to be calculated and the calculation type, such as addition or subtraction.PHP calls this method as a "hook" when it finishes a calculation and returns the result to determine the class of the return value.
But I don't like this because it's a "hacky" way of doing things with zend. Also, I am concerned about the cost of doing this for every calculation.
The reason I'm putting together what I consider to be a bad method is because it's possible that you or someone else will take my idea, add a twist that I haven't thought of, and come up with a way to make it all work.
Interesting.
Regards,
Saki
— Rob
Hi,
- The
static
distinction is also fairly meaningless, as in PHP there is no situation possible where an operator overload can occur WITHOUT it operating on objects themselves.For this, that is the wrong approach. The actual behavior is on the type, not the instance. The object instances may not even know their value, they merely represent the value.
A GMP object instance that does not know its value? What are you even talking about? Can you show me some code explaining what you mean? I had literally months of this argument for the operator overloads RFC, and studied the overload implementations in six other languages as part of writing that RFC, I feel like I understand this topic fairly well. But I do not understand what you are saying here.
A few minutes ago, I sent an email where I accidentally made the code non-static, and I think I see the merit in what you are saying. It felt so natural to use $this that I didn't even realize I was doing it wrong.
So, looking at your RFC and mine, I think this can be improved.
What would you suggest it look like and then we can work backwards from there?
Here are my thoughts on your code.
In theory, inheriting from this "improved GMP class" would allow
overloading of computational operators.In effect, this acts like a "calcable interface", with the constructor
passing in meaningless values to the parent constructor and the add
method allowing the user to freely modify the return value.This means that virtually any userland class can use the operator
overloading feature via a "hack".This approach is completely wrong. Rather than proposing this as is, it
would be more constructive to propose support for operator overloading.Regards,
Saki
For clarity (since I know from experience it's helpful to RFC authors to have a concrete sense of votes in advance): I will be voting No on this RFC. As both Jordan and Saki have explained, it's a hideous hack that doesn't look like it would even work, much less be wise. I'd much rather take a second swing at Jordan's original operator overloading RFC, which I supported and still support. Let's do it right.
--Larry Garfield
For clarity (since I know from experience it's helpful to RFC authors to have a concrete sense of votes in advance): I will be voting No on this RFC. As both Jordan and Saki have explained, it's a hideous hack that doesn't look like it would even work, much less be wise. I'd much rather take a second swing at Jordan's original operator overloading RFC, which I supported and still support. Let's do it right.
I agree with Larry that I would rather take another look at an overall operator overloading RFC than to implement this in one specific extension.
Cheers,
Ben
For clarity (since I know from experience it's helpful to RFC authors to have a concrete sense of votes in advance): I will be voting No on this RFC. As both Jordan and Saki have explained, it's a hideous hack that doesn't look like it would even work, much less be wise. I'd much rather take a second swing at Jordan's original operator overloading RFC, which I supported and still support. Let's do it right.
I agree with Larry that I would rather take another look at an overall operator overloading RFC than to implement this in one specific extension.
Cheers,
Ben
Thank you Larry and Ben, I appreciate it. Out of curiosity, do you consider ArrayAccess and Stringable a hideous hack too?
— Rob
Hello internals,
I'd like to introduce a new RFC: https://wiki.php.net/rfc/operator_overrides_lite which extends the GMP extension to support a limited set of operator overriding to developers. It's designed to be limited and relatively simple, while still being quite powerful. It would only be available if you have the GMP extension installed and enabled, but would allow for creating powerful unit libraries (such as representing money, duration, etc)
The design of this RFC is non-sensical.
If you want to do operator overloading via an extension, then create a dedicated extension instead of piggy-backing on an already existing extension,
this makes even less sense considering the extension is not always available.
You wouldn't even need an RFC as you could just write an extension and publish it on PECL and see if people adopt it.
As such I will be voting no on this proposal.
Sincerely,
Gina P. Banyard
Hello internals,
I've updated the RFC to include final-ish examples (barring any further constructive feedback), a prototype implementation, and an objections section.
Cheers,
Rob
Hi,
Hello internals,
I've updated the RFC to include final-ish examples (barring any further constructive feedback), a prototype implementation, and an objections section.
Cheers,
Rob
It seems like the "hack" I mentioned is still possible, am I misunderstanding something?
And I don't understand the purpose of polyfills at all. If you're not using the GMP extensions and can't do operator overloading, won't you just have a class with protected methods that are never used and don't actually do anything?
Regards,
Saki
Hi,
Hello internals,
I've updated the RFC to include final-ish examples (barring any further constructive feedback), a prototype implementation, and an objections section.
Cheers,
Rob
It seems like the "hack" I mentioned is still possible, am I misunderstanding something?
That’s always going to be a possibility, no matter what we do or how we do it. I think it would be a rather pointless hack now that I can run the code. For the most part, the engine treats these as numbers and trying to dodge that will land you in hot water eventually.
And I don't understand the purpose of polyfills at all. If you're not using the GMP extensions and can't do operator overloading, won't you just have a class with protected methods that are never used and don't actually do anything?
Ah, that could probably be clearer in the RFC, but you have to make it public to be able to use it.
It’s a bit clunky to use without the extension, but that’s mostly because I didn’t want to get into OperandPosition from Jordan’s RFC. Many people were confused about it, so I’m just avoiding it.
— Rob
Hi,
Hello internals,
I've updated the RFC to include final-ish examples (barring any further constructive feedback), a prototype implementation, and an objections section.
Cheers,
Rob
It seems like the "hack" I mentioned is still possible, am I misunderstanding something?
That’s always going to be a possibility, no matter what we do or how we do it. I think it would be a rather pointless hack now that I can run the code. For the most part, the engine treats these as numbers and trying to dodge that will land you in hot water eventually.
After playing with the code and seeing what I could get away with, making the GMP class readonly
appears to prevent many abuses, so I have amended the RFC and prototype code.
And I don't understand the purpose of polyfills at all. If you're not using the GMP extensions and can't do operator overloading, won't you just have a class with protected methods that are never used and don't actually do anything?
Ah, that could probably be clearer in the RFC, but you have to make it public to be able to use it.
I've iterated it a few times in the text of the RFC.
— Rob
Hi,
It seems like the "hack" I mentioned is still possible, am I misunderstanding something?
That’s always going to be a possibility, no matter what we do or how we do it. I think it would be a rather pointless hack now that I can run the code. For the most part, the engine treats these as numbers and trying to dodge that will land you in hot water eventually.
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
And I don't understand the purpose of polyfills at all. If you're not using the GMP extensions and can't do operator overloading, won't you just have a class with protected methods that are never used and don't actually do anything?
Ah, that could probably be clearer in the RFC, but you have to make it public to be able to use it.
It’s a bit clunky to use without the extension, but that’s mostly because I didn’t want to get into OperandPosition from Jordan’s RFC. Many people were confused about it, so I’m just avoiding it.
This is very confusing me. Why does this need to be a child class of GMP?
Regards,
Saki
Hi,
It seems like the "hack" I mentioned is still possible, am I misunderstanding something?
That’s always going to be a possibility, no matter what we do or how we do it. I think it would be a rather pointless hack now that I can run the code. For the most part, the engine treats these as numbers and trying to dodge that will land you in hot water eventually.
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
Not unavoidable, but pointless. For example, I attempted to create a String class that used + for concatenation. This kinda works, but if you pass it to something that takes a string, you get the underlying number and not the string you were trying to store. This is because GMP takes over casting forcing you to stick to numerical constructs.
And I don't understand the purpose of polyfills at all. If you're not using the GMP extensions and can't do operator overloading, won't you just have a class with protected methods that are never used and don't actually do anything?
Ah, that could probably be clearer in the RFC, but you have to make it public to be able to use it.
It’s a bit clunky to use without the extension, but that’s mostly because I didn’t want to get into OperandPosition from Jordan’s RFC. Many people were confused about it, so I’m just avoiding it.
This is very confusing me. Why does this need to be a child class of GMP?
This is addressed in the current RFC text, if I missed something, please ask!
— Rob
Hi,
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
Not unavoidable, but pointless. For example, I attempted to create a String class that used + for concatenation. This kinda works, but if you pass it to something that takes a string, you get the underlying number and not the string you were trying to store. This is because GMP takes over casting forcing you to stick to numerical constructs.
I don't understand why you only consider the casting case. You can simply convert it to a string via a method. As long as don't use any casting at the end, users can implement it however they like. I don't think this is a pointless hack.
Also, allowing "hack" just because they're not useful is not a good idea.
Again, if such functionality is provided, it should be exposed as formal support for operator overloading.
This is very confusing me. Why does this need to be a child class of GMP?
This is addressed in the current RFC text, if I missed something, please ask!
I don't understand why the GMP RFC mentions environments where GMP is not used.
There are a few other points worth mentioning, but the existence of polyfills makes this especially confusing.
To be usable, the developer must override the desired operations and make them public
Is this referring to a polyfill? Or is this just a necessary step to override the overload?
Regards,
Saki
Hi,
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
Not unavoidable, but pointless. For example, I attempted to create a String class that used + for concatenation. This kinda works, but if you pass it to something that takes a string, you get the underlying number and not the string you were trying to store. This is because GMP takes over casting forcing you to stick to numerical constructs.
I don't understand why you only consider the casting case. You can simply convert it to a string via a method. As long as don't use any casting at the end, users can implement it however they like. I don't think this is a pointless hack.
Also, allowing "hack" just because they're not useful is not a good idea.
We could just delete php-src, grab a beer, and watch the sunset. I don’t think you’ll ever be able to stop some programmers from hacking things together to solve business problems though. I’ve “hacked” weakmaps in userland to make Hour(1) === (yes, there are three! Equals there) Minute(60).
Again, if such functionality is provided, it should be exposed as formal support for operator overloading.
Thank you for your opinion, this RFC doesn’t stop that from happening and is completely orthogonal.
This is very confusing me. Why does this need to be a child class of GMP?
This is addressed in the current RFC text, if I missed something, please ask!
I don't understand why the GMP RFC mentions environments where GMP is not used.
There are a few other points worth mentioning, but the existence of polyfills makes this especially confusing.
To be usable, the developer must override the desired operations and make them public
Is this referring to a polyfill? Or is this just a necessary step to override the overload?
I recommend reading up on what a polyfill is, and why they are useful, if you are confused. But to answer your question, no, it has nothing to do with the polyfill, it’s just a necessary step. The polyfill is just provided for completeness.
Regards,
Saki
— Rob
Hi,
Hi,
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
Not unavoidable, but pointless. For example, I attempted to create a String class that used + for concatenation. This kinda works, but if you pass it to something that takes a string, you get the underlying number and not the string you were trying to store. This is because GMP takes over casting forcing you to stick to numerical constructs.
I don't understand why you only consider the casting case. You can simply convert it to a string via a method. As long as don't use any casting at the end, users can implement it however they like. I don't think this is a pointless hack.
Also, allowing "hack" just because they're not useful is not a good idea.
We could just delete php-src, grab a beer, and watch the sunset. I don’t think you’ll ever be able to stop some programmers from hacking things together to solve business problems though. I’ve “hacked” weakmaps in userland to make Hour(1) === (yes, there are three! Equals there) Minute(60).
Again, if such functionality is provided, it should be exposed as formal support for operator overloading.
Thank you for your opinion, this RFC doesn’t stop that from happening and is completely orthogonal.
This is very confusing me. Why does this need to be a child class of GMP?
This is addressed in the current RFC text, if I missed something, please ask!
I don't understand why the GMP RFC mentions environments where GMP is not used.
There are a few other points worth mentioning, but the existence of polyfills makes this especially confusing.
To be usable, the developer must override the desired operations and make them public
Is this referring to a polyfill? Or is this just a necessary step to override the overload?
I recommend reading up on what a polyfill is, and why they are useful, if you are confused. But to answer your question, no, it has nothing to do with the polyfill, it’s just a necessary step. The polyfill is just provided for completeness.
Regards,
Saki
— Rob
I understand your point, and any further comment from me would probably be of little value to you. This will be my last post on this thread.
I will definitely vote against this RFC unless the issues I have pointed out are addressed. No matter how innocuous they may seem, I would rather not expose operator overloading in the form of such "hack".
Regards,
Saki
Hi,
Hi,
I'm not sure. Does this mean that such "hack" is unavoidable?
And I don't really understand what "pointless hack" means. This would make sense if operator overloading was already allowed, but it isn't.
Not unavoidable, but pointless. For example, I attempted to create a String class that used + for concatenation. This kinda works, but if you pass it to something that takes a string, you get the underlying number and not the string you were trying to store. This is because GMP takes over casting forcing you to stick to numerical constructs.
I don't understand why you only consider the casting case. You can simply convert it to a string via a method. As long as don't use any casting at the end, users can implement it however they like. I don't think this is a pointless hack.
Also, allowing "hack" just because they're not useful is not a good idea.
We could just delete php-src, grab a beer, and watch the sunset. I don’t think you’ll ever be able to stop some programmers from hacking things together to solve business problems though. I’ve “hacked” weakmaps in userland to make Hour(1) === (yes, there are three! Equals there) Minute(60).
Again, if such functionality is provided, it should be exposed as formal support for operator overloading.
Thank you for your opinion, this RFC doesn’t stop that from happening and is completely orthogonal.
This is very confusing me. Why does this need to be a child class of GMP?
This is addressed in the current RFC text, if I missed something, please ask!
I don't understand why the GMP RFC mentions environments where GMP is not used.
There are a few other points worth mentioning, but the existence of polyfills makes this especially confusing.
To be usable, the developer must override the desired operations and make them public
Is this referring to a polyfill? Or is this just a necessary step to override the overload?
I recommend reading up on what a polyfill is, and why they are useful, if you are confused. But to answer your question, no, it has nothing to do with the polyfill, it’s just a necessary step. The polyfill is just provided for completeness.
Regards,
Saki
— Rob
I understand your point, and any further comment from me would probably be of little value to you. This will be my last post on this thread.
I will definitely vote against this RFC unless the issues I have pointed out are addressed. No matter how innocuous they may seem, I would rather not expose operator overloading in the form of such "hack".
Regards,
Saki
Thanks, I would love to address your issues but you’ve given me nothing actionable to work with. Thank you for your time.
— Rob
After some consideration and lack of enthusiasm; I'm withdrawing this RFC.
— Rob