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