Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115690 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 34123 invoked from network); 11 Aug 2021 08:11:29 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 11 Aug 2021 08:11:29 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 953A81804C8 for ; Wed, 11 Aug 2021 01:42:07 -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=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-qk1-f174.google.com (mail-qk1-f174.google.com [209.85.222.174]) (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 ; Wed, 11 Aug 2021 01:42:07 -0700 (PDT) Received: by mail-qk1-f174.google.com with SMTP id f23so1461457qkk.13 for ; Wed, 11 Aug 2021 01:42:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20150623.gappssmtp.com; s=20150623; h=mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=NQgLjERXggBsFfsiqyA8JWMxdSilwy1v2ZCbFhckfsE=; b=RTaWt8NWWfJBkWMunZ38FdqVL/6uD+aGSMH8SLvV1R5ysULR/ORQVCxXB99ABGkpwy 7fKXZ5oX/K+M26lHbNnypgl8+T9Bwpi2DukF+XgNHGWTweygz1UFyr6eNsMJzic8EA5s 6PPpG1P78aNRu0yQQFh3HrpiZvKOK8TetWtqTtYPCUYBwiTNdKOppFoZ9HkNS7+8P8jt eVmP7mMtCrwU0ViL0gh963fMTALEYmrpfcp2uQ2YiebyyO9eQEkISnEDsCAqQbMYnSUN v26c/rijE57V2rTQavscjvxqcDQIc4t5ZfSgyhzra3Ol79oXYECWgfRIRJoy2V/hJ7fn Cc5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=NQgLjERXggBsFfsiqyA8JWMxdSilwy1v2ZCbFhckfsE=; b=GFbepztiT0mM2Wr43aCmvjJ0n3FmhyU+GfhiOWAcNuY/upuRClCh0pVekqdQ8hBI3E +oFQEvAePgThkN6WAr4BJUb+QBuwk5YiE5QT6QnRNPYQ+zHpVBHdbiYYyqzu9PJRja3P DyT11w1D0pQ38ewCGfSIeWs8clB0xk4QNMHSfZQUtUt5kIHfoGvnShbrheVEDYgJKKTY 0ZCx7Wj56f9LAhHt7KqUgEbevfW5Y8g97wbSVQSiRLlIG8Cb9+cP6+JPgFvxVvqd7730 1VAKSzmza7gBfwpToAtSj4FQzx8htM9ZYMO5Qrw7MdhW/A++zFnaFu399eWcQS5esh7j yOxg== X-Gm-Message-State: AOAM530MdXRDVfduPaifDIoD0XVTDxzlmcglU+shaDPupGWLTomGMlFC 4nnlr663hq5db3aEnToD7JkpTQ== X-Google-Smtp-Source: ABdhPJxE6IrlQNCp8bzTaD/sgghqqU6Zk3NCeDJ1i0jH/N2plecF+wV96Q5oqmV1xkMP3ufX3XF0dQ== X-Received: by 2002:a37:88b:: with SMTP id 133mr22425283qki.339.1628671326451; Wed, 11 Aug 2021 01:42:06 -0700 (PDT) Received: from [192.168.1.10] (c-24-98-254-8.hsd1.ga.comcast.net. [24.98.254.8]) by smtp.gmail.com with ESMTPSA id b12sm4089083qtx.88.2021.08.11.01.42.05 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 11 Aug 2021 01:42:05 -0700 (PDT) Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.7\)) In-Reply-To: Date: Wed, 11 Aug 2021 04:42:05 -0400 Cc: PHP internals Content-Transfer-Encoding: quoted-printable Message-ID: References: <94696d46-c4e6-406a-b859-89144bff31bf@www.fastmail.com> <3D7F74C1-F1A0-4258-8EE3-3C143B6B54EB@newclarity.net> To: Jordan LeDoux X-Mailer: Apple Mail (2.3608.120.23.2.7) Subject: Re: [PHP-DEV] Revisiting Userland Operator Overloads From: mike@newclarity.net (Mike Schinkel) > On Aug 11, 2021, at 12:06 AM, Jordan LeDoux = wrote: >=20 > Here is a quick and dirty example of just the complex number use case. = It > assumes that the operator overloads already are built in. A couple of > things of note: Excellent. Thank you for taking the time to do this. Very helpful. One thing I looked for at first but did not find was an actual example = of how you would use them vs. how they would be implemented. So I = forked it and created such a file to see if I could understand how they = would be used: = https://github.com/mikeschinkel/operator-overloading-rfc/blob/master/use-c= ases/complex-numbers/example.php It that what you envision? To create a ComplexNumber() do I first need = to wrap in Real() and in Imaginary()? Or where you intending I could = just pass numeric literals? If yes, PhpStorm said your type hints did = not allow it. > 1. The actual mathematical logic to cover all the possible input and = return > types is extensive. Though the execution time and complexity are quite > limited, the code complexity is high due to the number of = conditionals. Yes, the complexity overwhelmed me at first glance, but I don't think it = needs to be so complex, which I will get to below. > 2. I chose to implement the various operator overload functions in > different ways to illustrate different methods that might accomplish = the > same task in userland code. For instance, in some cases I typed = against the > SimpleNumber abstract, while in others I expanded SimpleNumber out = into its > concrete implementations, Real and Imaginary. Yeah, that kind of threw me to, because I looked at the code before I = read your email. And it still throws me because I'm not 100% clear how SimpleNumber is = supposed to behave.=20 1. In your examples would I use `SimpleNumber` anytime I see = `Imaginary|Real`? 2. I notice in SimpleNumber methods you pass SimpleNumber $self as a = first parameter to __add() and __mul() and in ComplexNumber you pass = ComplexNumber $self as a first parameter, but in ComplexNumber you never = use it. Obviously the __add() and __mul() signatures would need to be = equivalent, but I didn't grok why you didn't just use $this instead of = $self so I omitted that parameter in my fork. 3. I'm not sure why we even need a `Real` class, other than the fact PHP = doesn't (currently?) support operations on numeric literals and = imaginary numbers. Check my logic here but if PHP understood complex = numbers then `Real` could just be a `float`. But with just userland = operator overloading we have to wrap a float inside an instance of Real, = right?=20 4. It looks like you added a $left =3D bool parameter but never used it. = I assume the intent is that PHP for some reason might want to convert = `$x + $y` to $y->__add($x,true)? When would it need that? Seems that = the $left parameter just makes the signature more complex. I dropped it = from my fork. Also, this really cries out for userland type definitions and type = aliases (which can and IMO should be two distinctly different things, = btw.): `SimpleNumber|ComplexNumber|int|float $other` > 3. I only implemented the _add() and __mul() methods for this example, = as > the reality is that getting into some of the more complex operations = would > be so much code that it wouldn't be a digestible example. The __pow() > method for ComplexNumber would be in excess of 100 lines and also = require a > polar coordinate representation within the class, for instance. Sure. =20 But it would be nice if you could also stub out __pow() and any others = abstract methods so the full list of expected operations and their = parameters.=20 BTW, when I asked I wasn't expecting to see an implementation for any of = the methods, I was just asking for an interface. You went above and = beyond that. And, since you are writing code that won't run anyway, why not go ahead = and assume a `typedef`? That would greatly reduce the visual complexity = of your example: typedef Number: Imaginary|Real|SimpleNumber|ComplexNumber|int|float; > This isn't a working implementation, in that this code will produce = errors > if run in PHP (due to the absence of operator overloads). Please note = that > if we were to go the route of creating domain-specific classes to = cover > this use case, the actual classes would be many, many times longer = than > this. Yes, of course. > This is some of the simplest logic involved in this particular use > case, which is why I chose it as the example, and that perhaps = illustrates > why I am willing to explore such objects but do not believe they are = the > best choice at this time. So I promised to explain how to simplify the logic. This has nothing to = do with operator overloads so anyone reading who is only interested in = the RFC can stop reading now. -Mike ---- There is a concept called the "Happy Path"[1] which is " is a default = scenario featuring no exceptional or error conditions." It is often used = to refer to testing, but there is an emerging trend where people are = arguing that you should align the happy path with the left edge of your = code[2][3]. =20 A similar mantra is: Avoid Nesting. Code that is more nested is now = being recognized as more complex than less nested code[4]. With that in mind I refactored your SimpleNumber and ComplexNumber = classes using the following strategies: 1. Use the simplest if {} expressions you can. That means no `||` or = `&&` expressions 2. Wrap `||` and `&&` expressions into named methods, i.e. = `isRealNumber()` instead of `is_int() || is_float() || instanceof Real` = OR list them out as multiple if {} statements. 3. For `&&` you can also invert the logic into OR logic and create = multiple if {} statements, e.g. `$realPart->abs() !=3D 0 && = $imaginaryPart->abs` in ComplexNumber::__add() becomes two if {} = statements. 4. When it appears impossible to avoid nesting an additional level = because of the requirements of the logic then that code it telling you = to break it out into a named function to be called, e.g. addParts(), = multiplyParts(), multiplyRealPart() and multiplyImaginaryPart() for = ComplexNumber. 5. Remove all "else" statements to unravel the logic so that all = conditional branching logic is nested at most one additional level. 6. And this final part is controversial because so many developers have = bought in to dogma that obscures the approach. Fortunately there are = people like Linus Torvalds who have been able to see past anti-GoTo = dogma[5]. Basically the refactoring approach is to use a `goto end;` = instead of an early return. That provides numerous benefits which I = document here[6].=20 You can find the fork of your code that illustrates all those techniques = to reduce code complexity here[7]. After reviewing it I hope you will = agree that the algorithms become much more obvious when using this = strategy for coding. But again, this coding strategy has nothing to do = with operator overloading, it was just something I did to help me be = better able to understand your use-case example. =20 I'll submit a PR just on the off-hand case you want to pull in some or = all the code. [1] https://en.wikipedia.org/wiki/Happy_path [2] https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88 [3] https://maelvls.dev/go-happy-line-of-sight/ [4] https://ieeexplore.ieee.org/document/6693981 [5] = https://web.archive.org/web/20130305022050/http://kerneltrap.org/node/553/= 2131 [6] https://github.com/mikeschinkel/go-only#benfits [7] = https://github.com/mikeschinkel/operator-overloading-rfc/tree/master/use-c= ases/complex-numbers=