Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122982 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 9D0B51A009C for ; Fri, 5 Apr 2024 19:42:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1712346174; bh=YoKfZ4+aRkpEgu2Ky9oWhS3PPyZVp8oqq/z2GCQTY2c=; h=Subject:From:In-Reply-To:Cc:Date:References:To:From; b=agNNM44eZgTNMn4uOaKQ2CfsaVJFzxE0+haQz+wf32m+wQseTNj3io0b0zUvSNG0+ iWuuC2eKaB2R7AiU8h7mueNPkzKja+rBu6aeRlwl2t3svtqCJhayYEnMKOBvRLn7Mo TNVrFxi3lsabd4t8jWiY/qwKD9YS6Ojysn6KWMLF5WvfkzuLPhZiBK4vYv5EHqgoHD 4/JHeCKTBdh4AQaRUlhLQUyPA+csgkXDjYN9F0xvCiF75iNSKUwWHRoP6aioyvPcJS JqnsKz1W8TkHlNwI10PJCtyYfxmmQkP3ybE32t49hX87ExqLFyTzqKILF8mQz/YoGe zqNmiYWfC2AYQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7249B180677 for ; Fri, 5 Apr 2024 19:42:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.5 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,HTML_MESSAGE, MIME_HTML_ONLY,MIME_HTML_ONLY_MULTI,MIME_QP_LONG_LINE,MPART_ALT_DIFF, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail.sakiot.com (mail.sakiot.com [160.16.227.216]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 5 Apr 2024 19:42:51 +0000 (UTC) Received: from smtpclient.apple (unknown [117.55.37.250]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by mail.sakiot.com (Postfix) with ESMTPSA id 0779C401CC; Sat, 6 Apr 2024 04:42:19 +0900 (JST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=sakiot.com; s=default; t=1712346139; bh=YoKfZ4+aRkpEgu2Ky9oWhS3PPyZVp8oqq/z2GCQTY2c=; h=Subject:From:In-Reply-To:Cc:Date:References:To:From; b=JTB5PqvscNPxjZEBxqqC6rti/1/ntCON8bNkGlxvhSez/MJ88Na/m3nzPvC5RtLQ+ JzsOivogGohCKPJvyfWe8FKBqjGdSvvIgsEMCgLgVmCbJMc+sdOKoRVZ6ZxOZ8UUAB Ds12FTmyrzQ6YtbiA5lZOPS+PesM9c+hQQQaoum4= Content-Type: multipart/alternative; boundary=Apple-Mail-423AF492-CF66-436F-9E8E-1EE575935F4A Content-Transfer-Encoding: 7bit Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (1.0) Subject: Re: [PHP-DEV] [RFC] [Discussion] Support object type in BCMath In-Reply-To: Cc: =?utf-8?Q?Tim_D=C3=BCsterhus?= , Lynn , Aleksander Machniak , php internals Date: Sat, 6 Apr 2024 04:42:06 +0900 Message-ID: <46B14427-44B2-4665-BC14-6018AC34B0CF@sakiot.com> References: To: Jordan LeDoux X-Mailer: iPhone Mail (21D61) From: saki@sakiot.com (Saki Takamachi) --Apple-Mail-423AF492-CF66-436F-9E8E-1EE575935F4A Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
Hi,

On Fri, Apr 5, 2024 at 2:48=E2=80=AFAM Tim= D=C3=BCsterhus <tim@bastelstu.be= > wrote:
Hi
=
Your `Money` example would allow for unsound and/or non-sense behavior,
= such as:

     $fiveEuros =3D new Money(5, 'EUR');
     $tenDollars =3D new Money(10, 'EUR');

     $what =3D $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))-&g= t;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


Well, since they are both in Euros, I w= ould expect 15, but I expect that was a typo considering the variable name a= nd it was meant to be 10 USD.

As I said, you can ac= complish the same through composition. For a money class, you'd likely need a= n extended method that checks the currency first, and then makes a call to t= he actual calculation method, which is essentially what the composing class w= ould do as well, something like `addCurrency()`.

Fra= nkly, I don't think that making it final actually makes the API more resista= nt to errors, but it's also not something I care about enough to really figh= t for it on this RFC. I think it's the wrong choice, because the one example= that I pulled up in this discussion does not constitute the entire breadth o= f use cases for this type of object, and I find myself extremely hesitant to= suggest that we have thought of and considered all the various use cases th= at developers might have, or how they might be impacted by making the entire= class final.

Making it final will not reduce error= s in my opinion, it will just make internals feel like those errors are less= "our fault". A composed class does not somehow prevent the accidental error= of mixing currencies, it just moves where that error would occur. Forcing c= omposition drastically reduces the usability of the operator overloads, whic= h I am opposed to, and I do not believe that this is being given the same ki= nd of consideration. Once the class is forced to be composed, it can no long= er be used as part of any kind of extensions either.

I mentioned that I have a library that adds arbitrary precision functions f= or trigonometric functions (among others), to extend BCMath (and ext-decimal= ). A library that is solely focused on additional arbitrary precision math f= eatures should be the first in line to use this RFC, but I can tell you that= if this class is final, I won't even update my library to use it at all, be= cause there will be no point. The reason I did not mention MY use case is be= cause I don't think many PHP developers are out there maintaining their own c= omplicated trigonometric extensions to BCMath, so I don't think it's a good e= xample of a common use case.

Making it final a= lso breaks with how other internally provided classes have been done in the p= ast, many with no discernable issues. I do not see any mailing list discussi= ons about the problems with DateTime being open for extension.
If you want an actual answer about how a Money class would actua= lly work in practice, it would likely be something like this:

=
```
// $val1 and $val2 are instances of the Money class= with unknown currencies
$val1Currency =3D $val1->getCurrency()= ;
$val2Currency =3D $val2->getCurrency();
$val1 =3D $= val1->convertToCommonCurrency();
$val2 =3D $val2->convertToC= ommonCurrency();
// perform the necessary calculations using opera= tors
$answer =3D $answer->convertToCurrency($userCurrency);
```

I would expect that most applications de= aling with money are converting to a common calculation currency prior to ac= tually doing any calculations, instead of relying on the conversion magicall= y happening inside their calculation methods.

So, y= our argument leaves me entirely unmoved. However, as I said, while this make= s the RFC essentially useless to me, I don't think it's worth rejecting the R= FC over, so I'll leave it at that.

Jordan
=

I've thought twice about making classes final.

The return type issue can be solved by specifying st= atic, but as Barney mentioned, there is a problem with operator overloading,= for example addition, where the class of the resulting object changes if th= e left and right operands are swapped.

In other wor= ds, if that problem can be solved, it may be possible to make a class inheri= table without making it final.

The only solution I c= an think of at the moment is to impose the constraint that when computing op= erator overloading, if the operands are both objects, they must be of the ex= act same class.

When calculating using a method, it= is clear which object should be prioritized, so there is no risk of breakin= g down even if the method accepts objects of different classes as arguments.= It would be possible to unify the behavior of the methods with operator ove= rloading, but this would be undesirable because the arguments would be contr= avariant.

I think my idea is somewhat better than u= sing final classes in terms of freedom for the user.

Whether it's a good API or not may require some further discussion.
<= div>
However, I think it is unlikely that a user will misunder= stand and introduce a bug into their code because an error will occur before= it will act as a bug. Alternatively, a user may misunderstand that a method= can only receive objects of the same class as an argument, but this only re= stricts the degree of freedom and does not create a bug in itself.

The only concern is that when do a calculation like `$numChi= ld->add($num)`, users might misunderstand that it returns the class of th= e argument object.

I'm a bit unsure if this could b= e a solution as there may still be something I've overlooked.

=
Regards.

Saki
= --Apple-Mail-423AF492-CF66-436F-9E8E-1EE575935F4A--