Newsgroups: php.internals
Path: news.php.net
Xref: news.php.net php.internals:122954
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 2845B1A009C
	for <internals@lists.php.net>; Fri,  5 Apr 2024 00:20:30 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail;
	t=1712276459; bh=owOCp/5Z2SCY//i+8EBnWHsV+Pq0rB7Du9oFwO3FXEU=;
	h=Subject:From:In-Reply-To:Date:Cc:References:To:From;
	b=ddiaIJSxmqOAc6V1mpPF0PcPTz7oD03Z4qAJlAvi1CCO0qRf8dBX9RPOXKgmqyRqm
	 bhGdQ1rLLyrOOPUnnDG7liT8ve7SQpPzLPqNdGAFK/11KMaK/vKaVpmgMaMms8RJDf
	 XVK0OEjc6dTMywk305uXgWo/8nB41Zv5DIqSBFTUILQaPorTmBNZSD6T6pIC6pw1/R
	 UAqGsTHWG/OiBB8Kr23wWz0c4m+Cw594Sls6AT7CVk1fti7QnDOal8/bSZ+gh8HDUq
	 19f1zY31YcMdB0atV6mLWZ+YZRLF1BEmM8wNwOwsYK8TT4hxqTYKwpT1Sc/u7DV3ew
	 flgtsgTv90siA==
Received: from php-smtp4.php.net (localhost [127.0.0.1])
	by php-smtp4.php.net (Postfix) with ESMTP id 9EADA18003E
	for <internals@lists.php.net>; Fri,  5 Apr 2024 00:20:57 +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=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED,
	DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,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: <saki@sakiot.com>
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 <internals@lists.php.net>; Fri,  5 Apr 2024 00:20:56 +0000 (UTC)
Received: from smtpclient.apple (unknown [117.55.37.250])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(No client certificate requested)
	by mail.sakiot.com (Postfix) with ESMTPSA id 740E6401D9;
	Fri,  5 Apr 2024 09:20:24 +0900 (JST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=sakiot.com;
	s=default; t=1712276424;
	bh=owOCp/5Z2SCY//i+8EBnWHsV+Pq0rB7Du9oFwO3FXEU=;
	h=Subject:From:In-Reply-To:Date:Cc:References:To:From;
	b=NRFnnXzDRIW/xtZicvBJP0fU7N0GMEzolameOCudlsIU97ZPRDQM7zDMKeHchD/UU
	 FuJ3gsvv30TPgwJkFPkVub3wurjuanZCXBsliu8SdLppus1Pdk+MSWVEP2sYOv34lX
	 U1IgMltKgyXfJgn5Kdi3oafVX8PhuMYZInskV+VY=
Content-Type: text/plain;
	charset=utf-8
Precedence: bulk
list-help: <mailto:internals+help@lists.php.net
list-unsubscribe: <mailto:internals+unsubscribe@lists.php.net>
list-post: <mailto:internals@lists.php.net>
List-Id: internals.lists.php.net
Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.700.6.1.1\))
Subject: Re: [PHP-DEV] [RFC] [Discussion] Support object type in BCMath
In-Reply-To: <CAMrTa2GsydsTtorY9hcEcjDZe2Uu+mRo8Wz9+0Dedc4OCdLtUg@mail.gmail.com>
Date: Fri, 5 Apr 2024 09:20:11 +0900
Cc: Barney Laurance <barney@redmagic.org.uk>,
 internals@lists.php.net
Content-Transfer-Encoding: quoted-printable
Message-ID: <92F7B1F2-67EE-4276-ADE0-20181B57DF5E@sakiot.com>
References: <4F094EDA-5058-407D-AF39-06FD934FDE1F@sakiot.com>
 <bbcff629-4dd6-bc50-ac9f-641849dc218a@php.net>
 <68CF373E-6ABF-4471-8992-813B4BA1B508@sakiot.com>
 <904197f4-afb5-401e-9e17-7a655c5449d0@alec.pl>
 <CAMrTa2EK6ymBQuxeFj7tMV0M2crqCCCR=mLN-FSfuCgrQHEZ6A@mail.gmail.com>
 <AB663F3F-A888-45F7-B19A-00530A4EDB0C@sakiot.com>
 <CAMrTa2HOEUWQcPdZE3FZ9JWCS1DWuRTNhhfqGGZOmr=v1Mctdw@mail.gmail.com>
 <CANS-=pfVuKKfAiu0LUJTrEPnSRaa_nHwJCgMvSORruAwHMsvQg@mail.gmail.com>
 <CAMrTa2HXD+62TKbaaA-v-mct5zdR5ortUAbtG1R1D22amAgpNA@mail.gmail.com>
 <CAMrTa2GaK3AUp0WodhNO8RdvoLbbiCk14Gs2oWF6vEs_+qMYNg@mail.gmail.com>
 <655FEA80-9AB4-4AAD-A310-70ED968C97A2@sakiot.com>
 <CAMrTa2Fh_-M_Kezi7s_4P4+a+dTyKq=RExd6ARPRDqZYS175kA@mail.gmail.com>
 <CAMrTa2FrkvNPK6kETNJFNNDn0mL5dxkV=PmdxsysruhhjY=Uig@mail.gmail.com>
 <8FB87901-02D7-4934-9119-55B21CEDDA9D@sakiot.com>
 <CAMrTa2EJf2NK2D3J=q7xkAVuNKsjEBDVv+RDmxp_g9DSsjRgwg@mail.gmail.com>
 <65e0fc8b-ed4e-4bf8-af2d-77dc7b4bb934@redmagic.org.uk>
 <CAMrTa2GsydsTtorY9hcEcjDZe2Uu+mRo8Wz9+0Dedc4OCdLtUg@mail.gmail.com>
To: Jordan LeDoux <jordan.ledoux@gmail.com>
X-Mailer: Apple Mail (2.3731.700.6.1.1)
From: saki@sakiot.com (Saki Takamachi)

Hi Barney, Jordan,

I would like to thank you all again for discussing this RFC in great =
detail. I believe that the specification for the `BCMath\Number` class =
is in very good shape due to the detailed discussions and the great =
amount of feedback. Now that we have been able to brush up the =
specifications so far, I would be very happy if we could have a good =
discussion about the concerns that still exist.

> Coming back to this point, I think these are basic features that =
people would expect to be there - I think I would find just slightly =
frustrating to start learning how to use a class like this and then
> find that it doesn't have these functions. Casting and calling =
`intval` or `floatval` all feel like slightly awkward workarounds that =
shouldn't be needed in a greenfield project. We know that the string
> inside the object is always a numeric string, so it should be easier =
to parse it as an int than to parse it as a date or a JSON document. =
Code doing the latter should stand out as odd looking.

> The class cannot guarantee that it can return a value in the type you =
request however, so the way that is handled would need to be decided. =
The value can easily be outside of the range of an int. Should it return =
a float silently in that case for `toInt()`? What if the value is beyond =
the range of a float? That would be a very rare situation, as floats can =
represent extremely large numbers (with very reduced accuracy), but I =
would expect it to throw an exception if that happened. Ideally an =
exception that I could catch and ignore, since I can almost surely deal =
with that error in most situations.
>=20
> What about a number that is so small that it can't fit in a float? =
Similar situation, though I expect it would occur slightly more often =
than a number being too large to fit in a float, even though it would =
also be rare.
>=20
> I think these helper functions belong in the RFC, but they aren't =
quite straightforward, which is what I think Saki was alluding to.

> Yes I agree there are subtleties to work out for these functions. I =
don't think there's such a thing as a number to small to fit in a float. =
Converting from decimal to float is always an approximation, sometimes =
the best approximation available as a float will be either 0 or -0.

If there is a demand for such functionality, I think it should of course =
be included in the RFC. However, as Jordan mentioned, proper error =
handling is required. IMHO, I think the following specifications are =
good:

- toInt(): If it has a decimal part, round it to an integer according to =
the rounding mode. If the result is too large to be represented as an =
int, it will not be converted to a float and will throw an exception =
meaning it cannot be converted to an int.
- toFloat(): If the result is too large or too small, throws an =
exception meaning it cannot be converted, just like int. Also, if the =
value is within the representable range but the precision is too fine, =
such as `1.0000000000000001`, it will be rounded according to the =
rounding mode. However, if round to a precision of 17 or 18 digits, the =
error will end up being very large, so refer to the value defined in =
`PHP_FLOAT_DIG` and use 15 digits as the maximum number of digits. This =
constant is 16 for IBM and 15 for others, but for consistency of =
operation it should be set to 15.


>> I don't think it will be possible to make a good Money class as a =
child of this. BcNum is a read-only class, so if the constructor of =
BcNum is final then the child class won't be able to take the currency =
as a constructor param, and won't be able to protect the invariant that =
currency must be initialized. If the constructor of BcNum were made =
non-final then BcNum wouldn't be able to protect the invariant of the =
numeric value always being initialized once the constructor has =
returned. And it should be straightforward to make a good money class as =
a composition of BcNum and a currency enum or string.
>=20
> The RFC does not list the constructor as final, and I would not expect =
it to.=20

>> There's probably not any good use case for a subclass to add =
properties, unless perhaps the subclass developers are willing do do =
away with some of the checks that would normally be done on a value =
object by the PHP runtime to keep it valid (maybe replacing them with =
static analysis checks). But there are lots of reasonable use cases for =
subclasses to add methods, even if they're effectively syntax sugar for =
static methods with a BcNum typed param.
>=20
> I literally just provided in the quote you are replying to a good use =
case for a subclass. You can do the same thing with composition yeah, =
and that might even be better to a lot of people, but I don't think this =
RFC should take a position AGAINST subclasses and in favor of =
composition.=20

>> Having just written that I've now checked the behavior of DateTime - =
see https://3v4l.org/5DQZg. The constructor of that isn't final, so a =
child class can replace it with an empty constructor. But if it does =
that and leaves the value uninitialized then it blows up on a call to =
`format`. That's probably not something we should emulate. =
DateTimeImmutable behaves the same way.

> Why not? Writing something like that obviously does not work, and the =
error would be immediately apparent. Is it possible to create something =
that will error? Of course. That's not an error that needs to be =
aggressively guarded against, because the feedback is rapid and obvious.


I don't think protecting initialization by the constructor is necessary. =
https://3v4l.org/YroTN
This problem can occur with any class, not just `DateTime`. And I think =
this is the responsibility of the user's code, and the language just =
needs to throw an exception appropriately.

To clarify: In my opinion, I think that value objects provided by =
languages =E2=80=8B=E2=80=8Bare not intended to force immutability or =
design onto users, to provide functionality to users who expect them to =
behave as immutable value objects. Therefore, even if a child class that =
inherits from `Number` behaves in a way that is not immutable, it is the =
responsibility of the user and there is no need to prohibit it.

In other words, the concerns we are currently aware of are issues of =
responsibility on the user's side, and my opinion is that there is no =
need to give that much consideration. However, this is my personal =
opinion, and there may be more reasonable opinions.

Regards.

Saki=