Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115691 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 40659 invoked from network); 11 Aug 2021 08:55:13 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 11 Aug 2021 08:55:13 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 51F41180088 for ; Wed, 11 Aug 2021 02:25:55 -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.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS 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-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (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 02:25:55 -0700 (PDT) Received: by mail-lf1-f53.google.com with SMTP id g30so4342396lfv.4 for ; Wed, 11 Aug 2021 02:25:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=VDMZ9SpE927z0oiIWGw7OFxXZh+MsZe3DR9Rj1V0Cqc=; b=Av2J1LGAlbbNuopTKKeOC4olvAG3XSo9YsT9dbnTef8s076wT49Cn25+oGJopZoAJE tk/NgzryZYeiFhDTcA4PZB6qmk9P4CrgPibq3lPBKESg12A7B8DGdoo00CtfWBbdim5i h6OJ/4/EULErVOgzEOrIIIlt4/CpVnlG/lRwRFnZoiIVtMwh5iBT+6dZaDER8imcySPr dwFg6cVMFvGchzvc+924CP1LLOmmnOlj1cgqHzw/SijWfkeYn7iaz4Y9FdcYrV6RjQRW PSaUrv8YBxOAvaC7Sevf+AT3ArmerHU/GrAbiY8qYMLwQRakBZpimIcuoWbtu+MOXedF YB2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=VDMZ9SpE927z0oiIWGw7OFxXZh+MsZe3DR9Rj1V0Cqc=; b=XddHB3sz7xsCIK9sOQaZX8OCBoYpYTyoUBQWQOACO2x4PHV99sdswG/yqix+e9dr/j hIhP4CqMUiY/v7Hi9ewyqqOC7iAWCKDFmlWZvPRo38Xvh38zJTeceDVi8LAdefy5HOaZ lmhqWKP9gccjRzduCx/mc9ESOZgul1p9HPmGMJQrJBlxwpsWX5KfSfo4OSn+dldrMojA qfPCPKZOQQO7xp/VRAYLhQsMPLIudjujd85glckDx37LKigOqFv4PNIv7OoMRLPNPpNX kSKTgwzE1ANkI1ipFGRYpWEsfbEy75N7k8Ayw2lQaaJfSNWuOwpmOPZN0K3ekBcaaGed eW+A== X-Gm-Message-State: AOAM531XlpZF9pdCPnKhEpPBEUoM00KV+G4x9NylHAWk6N86s7CzxlCR a+ikYuz+sUXJU2hBxCCcO+1XOtm1WvtRHxDp+To= X-Google-Smtp-Source: ABdhPJx8Nlp85W126fQPSQ2OEKoCPyKJnABgrPwf3doY7TWCYsX0E2JWQa3FyJ4lKCyqts780BRUUzonn6e84Vt7DXs= X-Received: by 2002:ac2:5471:: with SMTP id e17mr23886806lfn.216.1628673952503; Wed, 11 Aug 2021 02:25:52 -0700 (PDT) MIME-Version: 1.0 References: <94696d46-c4e6-406a-b859-89144bff31bf@www.fastmail.com> <3D7F74C1-F1A0-4258-8EE3-3C143B6B54EB@newclarity.net> In-Reply-To: Date: Wed, 11 Aug 2021 02:25:51 -0700 Message-ID: To: Mike Schinkel Cc: PHP internals Content-Type: multipart/alternative; boundary="00000000000064da4605c9453487" Subject: Re: [PHP-DEV] Revisiting Userland Operator Overloads From: jordan.ledoux@gmail.com (Jordan LeDoux) --00000000000064da4605c9453487 Content-Type: text/plain; charset="UTF-8" On Wed, Aug 11, 2021 at 1:42 AM Mike Schinkel wrote: > > 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. > > In this particular implementation yes, however that is mainly to solve the problem of what happens when you add an Imaginary and Real together, which won't result in either class but instead ComplexNumber. It also allows SimpleNumber to stand in for the typedef Real|Imaginary, because I wanted this example to use only the features I plan to add in my RFC + 8.1. That means no combination types (SimpleNumber&Real)|int and no typedefs. > > 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. > > It doesn't need to be, no. But then I wrote the implementation in about 30 minutes. The actual implementations in my Complex Number library took me several months. > > 1. In your examples would I use `SimpleNumber` anytime I see > `Imaginary|Real`? > > You can when the argument or the statement could accept either, then use the concrete implementations when you need to differentiate. This is a method of getting around the absence of typedefs in PHP. > 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. > > This is to remain consistent with the RFC's proposed (current) syntax, which covers use cases that don't apply to this particular example. $self is useful because it can be type hinted as an argument. So SimpleNumber provides the __add() and __mul() methods for Real and Imaginary, and it typehints $self as SimpleNumber. However, for the __mod() method for example, the SimpleNumber class could type hint the argument as Real instead, since taking the modulo of an imaginary number might be something you error on instead. In this case, PHP would give the much more useful error related to type inconsistency, instead of something like 'Operator not supported' when used with an Imaginary instance, as it would inherit the $self instanceof Real restriction from SimpleNumber. > 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? > > In this case, the reason for Real is so that custom logic around combination with ComplexNumber and Imaginary instances can be implemented. If there were literals for both imaginary numbers and complex numbers, and they behaved correctly native with ints and floats, then no, there'd be no need. But again, I'm not entirely certain that putting an entire complex number library into core is necessary or preferrable. > 4. It looks like you added a $left = 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.): > > Yes, that is also something that I have heavily considered doing an RFC for, however I highly suspect that others will attempt them and be more qualified to tackle those issues than I would. You assumed the purpose of $left correct (which is also covered in the RFC doc in the same repository, which I highly suggest you read since it seems it's generating some avoidable confusion for you here). > > 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. > > I mean... I'll just link to my actual library for that. It requires so much more external logic. I would need to write the function that translates between polar and cartesian coordinate systems, then I would need to write the trigonometry equation that allows you to handle complex numbers to a complex power. For complex numbers to a real power I'd need to write a generalized FOIL function, and then for imaginary powers I'd need to handle rotations around the origin. If you are actually curious about the math, (and it is very fascinating to me so I understand), I'd suggest that you look at library implementations of pow for complex numbers. I really don't think that it would serve the purpose of being a demonstration as well as you think it would. Because... > > 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() != 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. > Basically the refactoring approach is to use a `goto end;` instead of an > early return. That provides numerous benefits which I document here. > I want to avoid this. :) The implementations in my actual math libraries are much more thorough and thought out. This is an example. If people want to see something that is closer to what would actually be done in a library, they should look at an actual library. So instead, that's what I'll link to: Here's the multiply method: https://gist.github.com/JordanRL/98cceb392ba5ba943462fe574f18de51 Here's the translateToParts method: https://gist.github.com/JordanRL/2c67acb3b5d3069c3a4d2f0448a480d1 Here's the PolynomailFunction: https://gist.github.com/JordanRL/673e357e7f5cf63bd4554fb3161c026b You can think of ImmutableDecimal as Real|Imaginary, but with methods such as $obj->isReal() and $obj->isImaginary() on it. This is just for the __mul() method. The intent of this example is to show the nature of the problems being solved, not to show the actual solutions. Think of this as syntactically correct pseudo-code, not an actual implementation. Jordan --00000000000064da4605c9453487--