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