On Wed, Aug 11, 2021 at 1:42 AM Mike Schinkel <m...@newclarity.net> 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

Reply via email to