Hello internals,
I've had this little library for a while (https://github.com/withinboredom/time), mostly as a place to experiment with the quirks of PHP and take it in weird places. Anyway, I've been experimenting with "strongly typed time" in attributes for a while now. At this point, it's a little weird but doable to use something like this:
#[MyAttribute(units: StandardSecond, amount: 5)]
... which is less than ideal. What I would really like is this:
#[MyAttribute(5 * StandardSecond)]
PHP doesn't support operator overloading, which, from the last RFC attempt, probably won't for quite a long time. Thus, I started experimenting with extending the \GMP class, which is perfectly allowed since it isn't final. For those that don't know, GMP implements operator overloading such that a GMP number times an int results in a new GMP number.
Surprisingly, I can get this to work, but I end up with a GMP number -- with the right value -- but not the right type. Hence this email. In essence, I am attempting to "back-door" my way into having operator overloading.
I would like to add static protected methods to the GMP class that are called for operations. In the event all objects are base-GMP objects, no behavior will change.
So, here are my questions:
- for built-in extensions, does this need an RFC?
- does anyone have any strong opinions against having the ability to have actual, typed units in PHP?
- should we revisit operator overloading instead of me hacking my way into it?
- should we revisit "constant expressions" in attributes?
— Rob
I am interested to see what people say to this. One of the primary reasons
I was given by no voters on my operator overload RFC was that it was not
useful enough to justify the complexity. Since then however we have had a
steady stream of people on list presenting their use cases that have run
into issues, and proposing hacks and workarounds.
- should we revisit operator overloading instead of me hacking my way into
it?
The primary reason I decided not to revisit the RFC with changes or updates
was that the conversations I had with voters led me to believe that this
was fundamentally unproductive to pursue. I would be happy to bring it
again if that changes, but I will not get involved in working on this
feature again unless I'm convinced the sentiment of voters has changed, or
at least has become changeable.
Jordan
Hello internals,
I've had this little library for a while (https://github.com/withinboredom/time), mostly as a place to experiment with the quirks of PHP and take it in weird places. Anyway, I've been experimenting with "strongly typed time" in attributes for a while now. At this point, it's a little weird but doable to use something like this:
#[MyAttribute(units: StandardSecond, amount: 5)]
... which is less than ideal. What I would really like is this:
#[MyAttribute(5 * StandardSecond)]
PHP doesn't support operator overloading, which, from the last RFC attempt, probably won't for quite a long time. Thus, I started experimenting with extending the \GMP class, which is perfectly allowed since it isn't final. For those that don't know, GMP implements operator overloading such that a GMP number times an int results in a new GMP number.
Surprisingly, I can get this to work, but I end up with a GMP number -- with the right value -- but not the right type. Hence this email. In essence, I am attempting to "back-door" my way into having operator overloading.
I would like to add static protected methods to the GMP class that are called for operations. In the event all objects are base-GMP objects, no behavior will change.
No, GMP not being final was a mistake, mainly due to it the first conversion from resources to opaque objects.
I was intending on making the class final via an RFC because no one extends it, as it is pointless (checked also on private codebases via Exakat).
AND This whole back-door idea was explicitly shut down in the BCNumber RFC that got accepted for 8.4.
I do not want a random extension being abused for this purpose.
If you want to add operator overloading by that way you can do something very simple, which is create a new extension which has a class with all the relevant method that overloads the do_operator to call the relevant methods on the class.
You've got your back-door without poluting the problem space of an unrelated extension which is not even required to be installed.
- should we revisit operator overloading instead of me hacking my way into it?
Yes.
- should we revisit "constant expressions" in attributes
I do not see how this relates to the problem space if we have the above or you use a new dedicated extension for this.
Best regards,
Gina P. Banyard
Hi,
Hello internals,
I've had this little library for a while (https://github.com/withinboredom/time), mostly as a place to experiment with the quirks of PHP and take it in weird places. Anyway, I've been experimenting with "strongly typed time" in attributes for a while now. At this point, it's a little weird but doable to use something like this:
#[MyAttribute(units: StandardSecond, amount: 5)]
... which is less than ideal. What I would really like is this:
#[MyAttribute(5 * StandardSecond)]
PHP doesn't support operator overloading, which, from the last RFC attempt, probably won't for quite a long time. Thus, I started experimenting with extending the \GMP class, which is perfectly allowed since it isn't final. For those that don't know, GMP implements operator overloading such that a GMP number times an int results in a new GMP number.
Surprisingly, I can get this to work, but I end up with a GMP number -- with the right value -- but not the right type. Hence this email. In essence, I am attempting to "back-door" my way into having operator overloading.
I would like to add static protected methods to the GMP class that are called for operations. In the event all objects are base-GMP objects, no behavior will change.
No, GMP not being final was a mistake, mainly due to it the first conversion from resources to opaque objects.
I was intending on making the class final via an RFC because no one extends it, as it is pointless (checked also on private codebases via Exakat).
AND This whole back-door idea was explicitly shut down in the BCNumber RFC that got accepted for 8.4.I do not want a random extension being abused for this purpose.
If you want to add operator overloading by that way you can do something very simple, which is create a new extension which has a class with all the relevant method that overloads the do_operator to call the relevant methods on the class.
You've got your back-door without poluting the problem space of an unrelated extension which is not even required to be installed.
- should we revisit operator overloading instead of me hacking my way into it?
Yes.
- should we revisit "constant expressions" in attributes
I do not see how this relates to the problem space if we have the above or you use a new dedicated extension for this.
Best regards,
Gina P. Banyard
I agree with Gina. Being able to change the class of a computed result of an inherited child class causes several problems.
The points raised in the BCMath\Number
discussion can be summarized as follows.
- If it is possible to perform calculations on parent Class and child Class, what should the resulting class be?
- Multiplying the distance class and the speed class should give you the time class.
- Similarly, multiplying two distance classes together should yield an area class.
- If only child classes have a property that should be specified in the constructor, how do you determine the value of this property in the result class? Perhaps it is possible to determine the value of the property, but in that case the commutativity of the computation should be lost.
These problems cannot be solved without significant complexity. And there is a possibility that it cannot be resolved in the first place.
Regards,
Saki
I agree with Gina. Being able to change the class of a computed result of an inherited child class causes several problems.
The points raised in the
BCMath\Number
discussion can be summarized as follows.
- If it is possible to perform calculations on parent Class and child Class, what should the resulting class be?
Ask the class what it wants to be?
- Multiplying the distance class and the speed class should give you the time class.
That would depend on the library’s implementation.
- Similarly, multiplying two distance classes together should yield an area class.
See above.
- If only child classes have a property that should be specified in the constructor, how do you determine the value of this property in the result class? Perhaps it is possible to determine the value of the property, but in that case the commutativity of the computation should be lost.
This can be handled via the child class in static methods, per the library specifications.
These problems cannot be solved without significant complexity. And there is a possibility that it cannot be resolved in the first place.
I very much disagree; there is very little complications in the extension. It’s actually quite simple:
If one value is scalar and the other an instance of GMP, for binary ops, ask the GMP class to perform the operation. (This is a one-linish change)
If both are GMP instances, in a binary op, ask the left-most one to perform the op. The default GMP implementation will delegate to the right-side if the right side isn’t a base-GMP instance, otherwise, business as usual.
And that’s pretty much it, other than defining the base-type ops. From there, you can implement any units library you want, with whatever behavior makes sense — so long as the base representation is a number.
If I remember the original operator overloading RFC, this is exactly what was asked for by the people who voted no. In theory, it should pass if brought as an RFC.
— Rob
I agree with Gina. Being able to change the class of a computed result of
an inherited child class causes several problems.The points raised in the
BCMath\Number
discussion can be summarized as
follows.
- If it is possible to perform calculations on parent Class and child
Class, what should the resulting class be?Ask the class what it wants to be?
- Multiplying the distance class and the speed class should give you the
time class.That would depend on the library’s implementation.
- Similarly, multiplying two distance classes together should yield an
area class.See above.
- If only child classes have a property that should be specified in the
constructor, how do you determine the value of this property in the result
class? Perhaps it is possible to determine the value of the property, but
in that case the commutativity of the computation should be lost.This can be handled via the child class in static methods, per the library
specifications.
How is the library supposed to do that in the absence of operator handler
functions that can be overridden, or at least called when an operator is
encountered?
Jordan
__
I agree with Gina. Being able to change the class of a computed result of an inherited child class causes several problems.
The points raised in the
BCMath\Number
discussion can be summarized as follows.
- If it is possible to perform calculations on parent Class and child Class, what should the resulting class be?
Ask the class what it wants to be?
- Multiplying the distance class and the speed class should give you the time class.
That would depend on the library’s implementation.
- Similarly, multiplying two distance classes together should yield an area class.
See above.
- If only child classes have a property that should be specified in the constructor, how do you determine the value of this property in the result class? Perhaps it is possible to determine the value of the property, but in that case the commutativity of the computation should be lost.
This can be handled via the child class in static methods, per the library specifications.
How is the library supposed to do that in the absence of operator handler functions that can be overridden, or at least called when an operator is encountered?
Jordan
That’s what this email is about: adding those functions to the base GMP class as static protected methods.
A time class would look something like:
static protected add(GMP $left, GMP $right) {
if($left instanceof self && $right instanceof self) return new self(parent::add($left, $right));
throw new LogicException('can only add time to time');
}
— Rob
Hi Rob,
I agree with Gina. Being able to change the class of a computed result of an inherited child class causes several problems.
The points raised in the
BCMath\Number
discussion can be summarized as follows.
- If it is possible to perform calculations on parent Class and child Class, what should the resulting class be?
Ask the class what it wants to be?
- Multiplying the distance class and the speed class should give you the time class.
That would depend on the library’s implementation.
- Similarly, multiplying two distance classes together should yield an area class.
See above.
- If only child classes have a property that should be specified in the constructor, how do you determine the value of this property in the result class? Perhaps it is possible to determine the value of the property, but in that case the commutativity of the computation should be lost.
This can be handled via the child class in static methods, per the library specifications.
These problems cannot be solved without significant complexity. And there is a possibility that it cannot be resolved in the first place.
I very much disagree; there is very little complications in the extension. It’s actually quite simple:
If one value is scalar and the other an instance of GMP, for binary ops, ask the GMP class to perform the operation. (This is a one-linish change)
If both are GMP instances, in a binary op, ask the left-most one to perform the op. The default GMP implementation will delegate to the right-side if the right side isn’t a base-GMP instance, otherwise, business as usual.
And that’s pretty much it, other than defining the base-type ops. From there, you can implement any units library you want, with whatever behavior makes sense — so long as the base representation is a number.
If I remember the original operator overloading RFC, this is exactly what was asked for by the people who voted no. In theory, it should pass if brought as an RFC.
— Rob
I see.
All of what I wrote are problems that arise when php doesn't provide a way to control them in userland.
Your proposal implicitly allows operator overloading in userland.
So your proposal essentially amounts to allowing operator overloading only for GMP classes.
If I understand correctly, this discussion is not centered on GMP, but on the topic of limited exposure of operator overloading functionality.
I believe this is something that requires some very careful discussion.
Regards,
Saki
Hi Rob,
I agree with Gina. Being able to change the class of a computed result of an inherited child class causes several problems.
The points raised in the
BCMath\Number
discussion can be summarized as follows.
- If it is possible to perform calculations on parent Class and child Class, what should the resulting class be?
Ask the class what it wants to be?
- Multiplying the distance class and the speed class should give you the time class.
That would depend on the library’s implementation.
- Similarly, multiplying two distance classes together should yield an area class.
See above.
- If only child classes have a property that should be specified in the constructor, how do you determine the value of this property in the result class? Perhaps it is possible to determine the value of the property, but in that case the commutativity of the computation should be lost.
This can be handled via the child class in static methods, per the library specifications.
These problems cannot be solved without significant complexity. And there is a possibility that it cannot be resolved in the first place.
I very much disagree; there is very little complications in the extension. It’s actually quite simple:
If one value is scalar and the other an instance of GMP, for binary ops, ask the GMP class to perform the operation. (This is a one-linish change)
If both are GMP instances, in a binary op, ask the left-most one to perform the op. The default GMP implementation will delegate to the right-side if the right side isn’t a base-GMP instance, otherwise, business as usual.
And that’s pretty much it, other than defining the base-type ops. From there, you can implement any units library you want, with whatever behavior makes sense — so long as the base representation is a number.
If I remember the original operator overloading RFC, this is exactly what was asked for by the people who voted no. In theory, it should pass if brought as an RFC.
— Rob
I see.
All of what I wrote are problems that arise when php doesn't provide a way to control them in userland.
Your proposal implicitly allows operator overloading in userland.
I wouldn’t go that far. First of all, it would only apply to numerical constructs and operations; there’s no way to override concatenation (so you can’t make concatenation act like a dot-product); I’m not considering things like less-than/greater-than or even equals. I’m only considering mathematical operators, like +, -, /, %, etc. And lastly, there’s no special syntax like you would expect from proper operator overloads.
If anything, I would consider this operator overloading, lite edition.
So your proposal essentially amounts to allowing operator overloading only for GMP classes.
Yes, this would basically amount to being able to have some typed control over types of numbers (aka, units) or in simpler words: limited overloading is available for anything that can be backed by a single numerical value.
If I understand correctly, this discussion is not centered on GMP, but on the topic of limited exposure of operator overloading functionality.
It’s centered on GMP because the resource converted to object was left non-final, thus providing a way to provide a “typed number” to the engine. This actually presents a unique opportunity to implement a limited form of operator overloading in a way that is strictly opt-in (must have the GMP extension), useful, and what the detractors wanted on the last RFC (no special syntax, limited scope, some don’t want it at all — this is opt-in). To me, this seems like exactly what was asked for.
I believe this is something that requires some very careful discussion.
I agree and also disagree. We have an already well-thought out and researched, yet declined, RFC on the matter that can be used for reference. This means there is probably a lot we don’t need to think too hard on. Then there is the fact that this is in an extension, and not a very common one. Who knows, maybe having a limited form of operator overloading might convince people that proper operator overloading would be beneficial.
I’ll go ahead and write up an RFC and then it can be discussed properly.
And fwiw, yes it “feels hacky af” but interestingly, also makes a lot of sense in the environment we find ourselves in.
— Rob
Hello internals,
I've had this little library for a while (https://github.com/withinboredom/time), mostly as a place to experiment with the quirks of PHP and take it in weird places. Anyway, I've been experimenting with "strongly typed time" in attributes for a while now. At this point, it's a little weird but doable to use something like this:
#[MyAttribute(units: StandardSecond, amount: 5)]
... which is less than ideal. What I would really like is this:
#[MyAttribute(5 * StandardSecond)]
PHP doesn't support operator overloading, which, from the last RFC attempt, probably won't for quite a long time. Thus, I started experimenting with extending the \GMP class, which is perfectly allowed since it isn't final. For those that don't know, GMP implements operator overloading such that a GMP number times an int results in a new GMP number.
Surprisingly, I can get this to work, but I end up with a GMP number -- with the right value -- but not the right type. Hence this email. In essence, I am attempting to "back-door" my way into having operator overloading.
I would like to add static protected methods to the GMP class that are called for operations. In the event all objects are base-GMP objects, no behavior will change.
No, GMP not being final was a mistake, mainly due to it the first conversion from resources to opaque objects.
I was intending on making the class final via an RFC because no one extends it, as it is pointless (checked also on private codebases via Exakat).
AND This whole back-door idea was explicitly shut down in the BCNumber RFC that got accepted for 8.4.
Oops, I extended it :p (pun not intended)
In all seriousness, I actually find it a little weird but the weirdness can be tucked away (check the readme file) for an ergonomic usage. It still needs fleshing out, but for any kind of units library, it is an amazing accident. I’d argue for keeping it or even embracing it.
— Rob
On Wednesday, 26 June 2024 at 18:24, Rob Landers rob@bottled.codes
wrote:Hello internals,
I've had this little library for a while (
https://github.com/withinboredom/time), mostly as a place to experiment
with the quirks of PHP and take it in weird places. Anyway, I've been
experimenting with "strongly typed time" in attributes for a while now. At
this point, it's a little weird but doable to use something like this:#[MyAttribute(units: StandardSecond, amount: 5)]
... which is less than ideal. What I would really like is this:
#[MyAttribute(5 * StandardSecond)]
PHP doesn't support operator overloading, which, from the last RFC
attempt, probably won't for quite a long time. Thus, I started
experimenting with extending the \GMP class, which is perfectly allowed
since it isn't final. For those that don't know, GMP implements operator
overloading such that a GMP number times an int results in a new GMP number.Surprisingly, I can get this to work, but I end up with a GMP number --
with the right value -- but not the right type. Hence this email. In
essence, I am attempting to "back-door" my way into having operator
overloading.I would like to add static protected methods to the GMP class that are
called for operations. In the event all objects are base-GMP objects, no
behavior will change.No, GMP not being final was a mistake, mainly due to it the first
conversion from resources to opaque objects.
I was intending on making the class final via an RFC because no one
extends it, as it is pointless (checked also on private codebases via
Exakat).
AND This whole back-door idea was explicitly shut down in the BCNumber RFC
that got accepted for 8.4.Oops, I extended it :p (pun not intended)
In all seriousness, I actually find it a little weird but the weirdness
can be tucked away (check the readme file) for an ergonomic usage. It still
needs fleshing out, but for any kind of units library, it is an amazing
accident. I’d argue for keeping it or even embracing it.— Rob
I don't, that's why I pushed to include operator overloads. In fact, one of
the libraries I was going to build (and have already partially built) was a
units library that automatically handled unit conversions, multiplications,
etc. by keeping track of everything in SI base units and providing
different value classes for different combinations of SI base units.
If you want to look at how I did that, I can email you the repo for that
code directly, though I haven't touched it in a LONG time because it's
mostly useless without operator overloads.
Jordan