On Wed, 2020-01-29 at 00:14 +0100, jan.h.boeh...@gmx.de wrote:
> the last days I have experimented a bit with operator overloading in
> userspace classes (redefing the meaning of arithmetic operations like

Some historic context: I am probably the one who did operator
overloading in PHP first. Oldest trace is this post:
https://markmail.org/message/y7rq5vcd5ucsbcyb



> 
> Here you can find some basic demo code using it:
> <https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6>
> https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6
> 

This example can be used to show a major problem for PHP doing this.

The first problem is that PHP historically had very few type
annotations making the code hard to predict, modern PHP has more of
that reducing this a bit, but the big problem remains: In PHP we can't
overload functions, thus operators have to be member functions and
therefore form a closed set.

In your example the vector3 can operate on vector3s. 

   $a = new Vector3(1, 2, 3);
   $b = new Vector3(3, 2, 1);

   $c = $a * $b;

Within Vector3 that is complete. But maths allows multiplication with
integers. So, yes, you can extend your __mul() with a check for the rhs
as you did with the is_numeric, but why would


    $c = 2 * $a;

call the Vecotr3's operator function? I believe it would call integer's
operator. Which obviously doesn't exist.

But okay, let's do a hack for integer, to call the second arguments
operator if first argument is an integer.

Now I come and really like your vector3 library and create my Matrix
type. With my Matrix i want to still use your Vector3.

   include 'your/vector3.php';
   class Matrix {
      public static function __mul($lhs, $rhs) { ... }
   }

   $vec = new Vector3(...);
   $matrix = new Matrix(...);

   $result = $vec * $matrix;

Which one is being called? - Vector's or Matrix's.  How will your
vector know about my Matrix?

The way C++ solves this is by allowing non-member functions as
operators.

     #include "vector3.h" // provides class Vector3
     #include "matrix.h"  // provides class Matrix, potentially
                          //  from a different vendor

     Matrix operator*(const Vector3 &lhs, const Matrix &rhs) {
         // I can provide  this myself if neither Vctor's nor
         // Matrix's vendor do
         return ...;
     }

     int main() {
         Vector3 vec{...};
         Matrix matrix{....};

          // works
          auto result = vec * matrix;
     }


To make this really work C++ has another magic, aside from function
overloading, which is ADL - Argument depending lookup, which is the
black magic of C++: If The function to be called is not only looked for
in the current or global namespace, but also the namespace of the first
Argument. So this works:


    namespace JohannesCoolLibrary {
        class Vector;

        void func(Vector v);
        Vector operator*(Vector lhs, Vector rhs);
    }

    namespace SomeOtherCoolThing {
        JohannesCoolLibrary::Vector v1{};
        JohannesCoolLibrary::Vector v2{};

        v1 * v2; // will find the operator in the namespace
                 // even though it's using the global name

        func(v1); // will also call function from argument's namnespace
    }

Without these features you can only create closed types, which
massively limit interoperability, which massively limits the use cases
for operator overloading.

With limited set of use cases, this is a rare feature, which is hard to
understand, or how many internals reads do you expect immediately know
the output of

    function a($a, $b) {
        return $a + $b;
    }

    var_dump(a([1], [2]));


johannes

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to