Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115767 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 87339 invoked from network); 20 Aug 2021 16:15:58 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 20 Aug 2021 16:15:58 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id C6A121804AA for ; Fri, 20 Aug 2021 09:48: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_H2,SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS11403 64.147.123.0/24 X-Spam-Virus: No X-Envelope-From: Received: from wout1-smtp.messagingengine.com (wout1-smtp.messagingengine.com [64.147.123.24]) (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 ; Fri, 20 Aug 2021 09:48:58 -0700 (PDT) Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.west.internal (Postfix) with ESMTP id 251753200924 for ; Fri, 20 Aug 2021 12:48:58 -0400 (EDT) Received: from imap43 ([10.202.2.93]) by compute1.internal (MEProxy); Fri, 20 Aug 2021 12:48: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=ZkeFla KM98rs6dyMFI0fc+G+3rIzUvlnedvQ8DWFZSk=; b=nQzXEGDInc8FMmCeUYaevj q3wIjPva7bnWF3RRXbT50aOpWEHsD2mgZvhPwGXHCaRGrod7hrcXDMikMkUWyz+Z oSrZiaor0ZbiA8IP9PuV7MecTfIWxAHxf9CAQw5PgqVLhfH59cW/WavlWbUhSOmQ +L51qm6z0lEEUkCMr79KLX6lDAgTlUO+eH1rMchyFiZc55Y0YdxUGrqGxSogvaUL cFCLc45JADnQ+adCrb1i1U6YrT6OmTXI2EN8wNk6dcMJlpt4DIAbSrZ+S7PfPooV Xi8mxD7dUeq3rQZzFiMKJtSv0nw0N+PJwX8n2MM8Nc/+22hQPvFuDFnfIdpo94Dg == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvtddrleelgddutdegucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepofgfggfkjghffffhvffutgesthdtredtreertdenucfhrhhomhepfdfnrghr rhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtoh hmqeenucggtffrrghtthgvrhhnpeefteevffetteeuueekgeejffduueehtdfhveefheei hfehgeehffehtdehgfeljeenucffohhmrghinhepphhhphdrnhgvthdpghhithhhuhgsrd gtohhmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhep lhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhm X-ME-Proxy: Received: by mailuser.nyi.internal (Postfix, from userid 501) id 73AC6AC0DD0; Fri, 20 Aug 2021 12:48:57 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.5.0-alpha0-1118-g75eff666e5-fm-20210816.002-g75eff666 Mime-Version: 1.0 Message-ID: <2fd2d665-ca3a-4fa8-9196-6c8b1b2a3da0@www.fastmail.com> In-Reply-To: References: Date: Fri, 20 Aug 2021 11:48:36 -0500 To: "php internals" Content-Type: text/plain Subject: Re: [PHP-DEV] [RFC] User Defined Operator Overloads From: larry@garfieldtech.com ("Larry Garfield") On Thu, Aug 19, 2021, at 3:38 AM, Jordan LeDoux wrote: > Hello again internals! > > Thank you all for the feedback that you've provided on my initial inquiries > about this feature. Several bits of feedback I've received have resulted in > changes to the proposal as it stands and made this a better proposal. > First, the RFC has been moved to the wiki: > > https://wiki.php.net/rfc/user_defined_operator_overloads > > The RFC is very long, as it contains a lot of research into the topic, > which is part of the reason I'm bringing it to discussion so early. Work > has also begun on an implementation, for which the draft PR can be viewed > here: > > https://github.com/php/php-src/pull/7388 > > As the PR indicates, there is still a ways to go before a finished > implementation is ready, however the RFC document appears complete enough > now to be opened for discussion. Again, as I've indicated before, I'm > taking my time with this, so there won't be a vote prior to a final > implementation being reviewable. > > It is possible that this RFC will involve some changes to opcache as well > as new opcodes for > and >= in order to maintain consistency of operand > precedence in execution, and that's something I want to take my time with > and listen to a lot of feedback on. Its impact is currently unknown as the > implementation for it is unfinished. > > Some key points for this proposal as it is currently: > > - TypeErrors from the operator methods are thrown immediately instead of > being suppressed. > - The parameter for the other operand must be explicitly typed; an omitted > type is not considered mixed for these methods. Reasoning explained in the > RFC. > - Type coercion still occurs as normal when strict types is not used. > - The return type of the == operator function is restricted to be a bool. > - The return type of the <=> operator function and all implied operators > (<, <=, >, >=) is restricted to be int and is normalized to -1, 0, 1. > - User classes are forced to implement overloads to work with the supported > operators, resulting in an InvalidOperator exception if unimplemented. > (This is a BC break, but impact is anticipated to be low) > - The <=> operator instead falls back to the existing comparison logic > if the overload is unimplemented. > - The special cases of $obj == null and $obj == false return the > expected bool value even if the overload is unimplemented. > - Internal classes silently fail-over to existing operator logic. (This > mainly affects ==). > - Enums are allowed to utilize operator overloads, but unlike other > classes, are not forced to and fall back to existing logic for comparisons. > - The proposal is for dynamic methods instead of static methods, with > reasoning explained in the RFC. > - The identity operator === is not overloadable to prevent possibly > terrible misuse. > - Interfaces are not suggested or proposed, with reasoning explained in the > RFC. This is an amazingly detailed and well-written RFC. Thank you! Comments in no particular order: - In the enum example, you can make it vastly simpler by copying my previous bitwise example from the previous thread, using ->value and ->from(). At the very least, all those if-else blocks should be match()es. - As you seem to be a fan of math terminology (hi friend!), I believe the formal way to state commuitivity is "multiplication is commutative over natural numbers". Some of the wording around that and associativity (Ibid.) would probably be cleaner with that phrasing. - The "Separate codebases" risk is odd. As you note, "the same as anything else, duh, copy-pasta will bite you." I'm not sure why that's even listed. - The larger risk would be competing implementations that use operators differently. Eg, I could see one collection implementation using / to indicate "remove these elements" (because that's the complement of modulo, which is "all elements except these"), and another to indicate "give me an array of these many equally-sized collection objects." That would be confusing if you bounce between two implementations on different projects. (Eg, Laravel and Symfony.) (We could argue that one of those implementations of / is dumb, but that doesn't mean they won't exist.) - I don't really grok how $queue -= 1 would work. If that expands to $queue = $queue - 1, then the = operator would return the value assigned ($queue, after the value is removed), and you would get the next item... how? I don't know how you'd do that, making that syntax nominally valid, but useless. - "In Python, the comparison operators are not directly commutative, but have a reflected pair corresponding with the swapped order. However, each object could implement entirely different logic, and thus no commutativity is enforced. " I... have no idea what that sentence means. Please clarify. - It doesn't seem to be explicitly stated, but I presume developers can type their operator methods as `mixed` if they are so inclined? (I don't see why they would, but they could.) - You refer to InvalidOperator as an exception, although it extends Error. That's technically incorrect. It is a new *throwable*, which extends error. Please use the correct terms. (The design is fine, IMO, but it should have the correct terminology.) - I think sticking to compareTo() is probably wise. I can see all sorts of abuses of the other comparison operators if they were allowed... and I'd probably do some of that abuse myself, frankly. :-) - Please make sure via tests that __compareTo() is triggered by sort() and friends, as that's the main use case for it: Making sorting objects just like sorting anything else, without having to provide a separate, independent comparison algorithm. (Basically, replacing usort() with __compareTo().) > Open questions: > > - Should this RFC include the bitwise operators or remain limited to the > minimal set of math operators? This should IMO be decided based on how easy they are to implement. If it's trivial to include, and uncontroversial, sure. If it's contentious or hard, punt to a later RFC. > - Should the == operator fail-over to existing behavior for all classes if > unimplemented, not just internal ones? (This would reduce BC break, but > make catching implementation errors more difficult for userland code.) It would help if you documented the current behavior in the RFC, because I had to go test it to see what it was. :-) I'd say yes, keep the existing behavior. I actually have a few tests right now in a serialization library that check if $foo == unserialize(serialize($foo)); (Not those functions, but in concept.) > - Should implicit interfaces be provided for the overloads? (Actual > interfaces cannot be provided except for if my previous Never For Parameter > Types RFC were adopted.) I think the RFC makes a good case that, unlike Stringable, they're not really useful. The reason to have them would be to ask "does this object implement this magic method", but as you note that doesn't really tell you much. Stringable only has one meaningful operation, so it's meaningful to type against. Addable doesn't give you sufficient information. (Insert the usual comments about Generics here.) Thanks! --Larry Garfield