Re: [RFC] [Discussion] Support object type in BCMath
| From: | Saki Takamachi | Date: | Fri, 05 Apr 2024 12:10:26 +0000 |
| Subject: | Re: [RFC] [Discussion] Support object type in BCMath | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-122965@lists.php.net to get a copy of this message | ||
Hi Tim, Barney,
> Your
Money example would allow for unsound and/or non-sense
> behavior, such as:
>
> $fiveEuros = new Money(5, 'EUR');
> $tenDollars = new Money(10, 'EUR');
>
> $what = $fiveEuros + $tenDollars;
>
> What would you expect to be in $what? A BcMath\Number(15)?
>
> ----------------
>
> The BcMath\Number class *should* absolutely be final, as a number is a number is a number.
> Allowing extension just to be able to write $number->isPrime() instead of isPrime($number) will
> allow for very confusing code, such as the example above, with no way to work around it in userland.
> It also makes interoperability between two different libraries that expect their own extensions to
> work very painful.
>
> Consider the following example:
>
> class PrimalityTestingNumber extends Number {
> public function isPrime(): bool { }
> }
>
> class ParityTestingNumber extends Number {
> public function isEven(): bool { }
> public function isOdd(): bool { }
> }
>
> If I now want to create a function to check whether a number is an even prime, I need to do
> something like this:
>
> function isEvenPrime(Number $n) {
> return (new PrimalityTestingNumber($n))->isPrime() && (new
> ParityTestingNumber($n))->isEven();
> }
>
> This use case would be much better solved in a generic way using something like this
> "Extension Methods" proposal:
> https://externals.io/message/118395#118395
> I've already sent a sibling email, explaining why I believe that making the Number class
> not final is a mistake. However I'd also like to comment on that specific bit of your email:
>
> I strongly believe in misuse-resistant APIs. Users should generally be steered towards making
> the right choice, even without needing to consult the documentation. For example, by making the
> "right choice" the easiest choice or by preventing "wrong choices" entirely.
>
> Preventing folks from making wrong choices is overall less costly than them realizing that they
> made a wrong choice and then being unable to change it, due to backwards compatibility or
> interoperability concerns.
>
> PHP has enough gotchas as it is, so for any new API making it a *great* API, not just an *okay*
> API should be part of the consideration. APIs within PHP need to survive for 10+ years.
Thanks for that very important point, Tim. I was designing classes with GMP in mind, so I overlooked
the point you mentioned.
For reference, classes that inherit from GMP return GMP.
> Uninitialized is miles better than 0 I think. 0 is a meaningful number just like any other and
> we should very strictly avoid inserting made up numbers into people's applications. Let them
> fail fast, not output fake data. I'd rather my web shop crashes than gives things away for
> free.
> Tim has convinced me that it should be a final class, in which case there's no meaningful
> distinction between a readonly class and a class with no mutable properties. In that case just for
> simplicity I'd say it should be a readonly class.
>
>
> If it's not a final class I think I'm not the right person to ask, since as I said I
> don't really like the fact that a readonly class behaves any differently to a class with no
> mutable properties. I prefer the behavior of the latter.
>
Due to the points Tim mentioned, I decided to make BCMath\Number a final class. And, as you say, for
clarity's sake I would make it a read-only class.
This also means that we don't have to worry about errors due to values not being set in
the constructor.
Regards.
Saki