Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115667 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 55615 invoked from network); 8 Aug 2021 15:09:59 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 8 Aug 2021 15:09:59 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E544F18053A for ; Sun, 8 Aug 2021 08:39:59 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS11403 66.111.0.0/20 X-Spam-Virus: No X-Envelope-From: Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 8 Aug 2021 08:39:59 -0700 (PDT) Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.nyi.internal (Postfix) with ESMTP id A2CB85C0134 for ; Sun, 8 Aug 2021 11:39:58 -0400 (EDT) Received: from imap43 ([10.202.2.93]) by compute1.internal (MEProxy); Sun, 08 Aug 2021 11:39:58 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=gjr4+B 5vezyEyidKeir6VckoOPrqImfuL44YII0iOJk=; b=V9wU7Kt0F05s7pgAvtSvcv xNQ2+xibOkAXNmewnR/A/Qk2I7YGBfOav7QccoBhnFvu0b26Mnm7RcuZkR1hn9Pl OhOKk6mRIcjVIUeyUoCNMK14g9Sq4rk8XoUvoLZ21oaT+OpeFTXuJem9/ZKvAfch YX0hP0C2Va6HQk0FCYn0ZlSlq81dKUQyjv1Lq/ikeg2enN4gSp8sDCK80KkmjwlQ Oo78fdppfTToDW6GAVzfKFfHa+WguEnOJom1/A4nqO6j1sDWxfeWDHYYg6ETu1Ad i0SKQVTWgvwVqmWtWcRV8PoVcQcwFPtXg9ssQOlTv1/tHdcYJ7Sfvb04DKVn9ztw == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvtddrjeehgdeludcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefofgggkfgjfhffhffvufgtsehttdertderredtnecuhfhrohhmpedfnfgrrhhr hicuifgrrhhfihgvlhgufdcuoehlrghrrhihsehgrghrfhhivghlughtvggthhdrtghomh eqnecuggftrfgrthhtvghrnhepgeelgfekudeivddvteffueejffdthfejieevhefgffek udevkedtvdelvddvffefnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrg hilhhfrhhomheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhm X-ME-Proxy: Received: by mailuser.nyi.internal (Postfix, from userid 501) id 2AAFDAC0DD0; Sun, 8 Aug 2021 11:39:58 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.5.0-alpha0-552-g2afffd2709-fm-20210805.001-g2afffd27 Mime-Version: 1.0 Message-ID: <1ac66cec-239d-489c-ab96-304cc1e376fd@www.fastmail.com> In-Reply-To: References: <94696d46-c4e6-406a-b859-89144bff31bf@www.fastmail.com> Date: Sun, 08 Aug 2021 10:39:36 -0500 To: "php internals" Content-Type: text/plain Subject: Re: [PHP-DEV] Revisiting Userland Operator Overloads From: larry@garfieldtech.com ("Larry Garfield") Responding to a bunch of people at once here, for simplicity: On Sat, Aug 7, 2021, at 7:04 PM, Jordan LeDoux wrote: > > The 4 arithmetic operators, concat, and compare. > > I would argue that both modulo and pow (% and **) are arithmetic operators > that should come with the basic four as well. Meanwhile, I think the concat > operator is both more prone to error *and* less useful. Currently, if you > try to concat an object, it will be cast to a string, and since > __toString() already exists, this behavior can effectively be controlled > already. The main reason that other operators couldn't easily be solved > with supplementary __to*() methods is that for *many* usecases where you'd > benefit from overloading operators, the scalar types don't adequately > capture the state (which is presumably the reason that the program is > representing them as objects). Fair point on concat. I am not sure that mod and pow are logical, except for completeness purposes. Either way, the general point is that we can and should start with a smaller set of operators to prove the concept, then expand it in additional RFCs. (Assuming that the pattern would be effectively the same for any operator once the plumbing is in place.) > Further, as detailed in my earlier reply, I don't think we can get away > with only allowing compare. Complex numbers are a trivial example of why, > but there are many others where only some of the comparison operators make > sense. We could, however, allow compare as a minimal implementation to > support the others. If it is implemented, then the others can be derived, > but if it is not then the other comparison operators should be able to be > implemented individually. Hrm. Having two different versions of comparable feels highly problematic to me, just from an understandablity point of view. We would have to try and figure out which if any combinations necessarily really do have to come together (eg, is implementing < but not > even possible?). I don't think there's a way to implement one of two mutually exclusive interfaces. I'd hate to push comparables to their own RFC as that's also been its own ball of controversy in the past, but... > As such, I think the idea of operator overloading is fundamentally > incompatible with the concern of ensuring commutability. In fact, *many* of > the usecases for this feature would *depend* on them not being commutable. > For matrices, A * B != B * A except under specific values. This is in fact > part of the definition of multiplication under matrices, so I feel that > wanting to ensure commutative behavior actually represents misunderstanding > the feature itself. Extending the native operators to work with matrices > using an internal implementation would *also* result in non-commutative > behavior, because that's the *correct* behavior for matrices. A good point. I would urge you to include the Matrix example in the RFC to head off any complaints about commutability. > As such I think that requiring static methods is a way to make internals > "feel better" about it without actually providing any real guarantees of > consistency or immutability. I concur. > > What if any type enforcement should the language force? > > Personally I do not think that any type enforcement should be required. I > think it should be *possible* if the application wants it, but I don't > think it should be required. So, that's essentially the __invoke() approach I suggested. I'm good with that. On Sun, Aug 8, 2021, at 2:41 AM, Jordan LeDoux wrote: > In any case, it is certainly possible that we could instead implement some > magic *objects* which can be extended that have built-in overloading in > specific ways. I think this is actually worse for the following reasons: An abstract base class-based implementation is an instant-no from me, full stop. Let's not and say we didn't. On Sun, Aug 8, 2021, at 6:25 AM, Jordan LeDoux wrote: > So while I'm aware of the concerns, I'm unconvinced they are founded in > reality. That is, I think the concerns about maintainability and complexity > may be unfounded in real world scenarios. However it is an extremely > consistent opinion here, and I would like to understand *why* it is viewed > as bad for the language in a more concrete way if that is possible. I can't speak for Ruby, but I suspect at least part of it is is C++. I have heard plenty of complaints about C++'s overloading implementation, which allows all kinds of arbitrary weridness. That's why I was arguing previously for a more named approach, as that provides a more Python-esque suggestion of how to use a given overload properly. Adopting Python's r*OP naming to handle rhs overloads is interesting, but I'm not sure I like it. It feels like it may just be begging for more complexity and weird behavior. But I'm open to further investigation of it. On Sun, Aug 8, 2021, at 8:11 AM, Rowan Tommins wrote: > Since even a single operator interface can't guarantee that all inputs > will have a valid output, I remain unconvinced that implementing 8 > different interfaces for the above is any better than implementing one > interface and stubbing the parts that have no valid inputs. 1) Implementing an interface and stubbing out some methods is a lie. It's a lie the engine won't stop you from telling, but it's still a lie. If you only support addition and division, for instance (add to a collection and split a collection into several), then having a bunch of comparison methods that your object says you support but the program dies as soon as you use it is a lie, and a landmine waiting to happen. 2) As Jordan demonstrated, there are many even formally defined situations where some of the operators are explicitly meaningless. It would mean in those cases you cannot use this feature without lying, and setting landmines for other developers. 3) If you really do want to use all of those methods, yes, it would mean 8 interfaces. However, there's 2 easy solutions to that: A) We already know that type aliases would be useful. B) We could make the interface implicit the way Stringable is. That way you can just implement the method and move on, but people could still type against Addable or Divisible or whatever. --Larry Garfield