Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108425 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 19623 invoked from network); 7 Feb 2020 22:35:40 -0000 Received: from unknown (HELO localhost.localdomain) (76.75.200.58) by pb1.pair.com with SMTP; 7 Feb 2020 22:35:40 -0000 To: internals@lists.php.net References: <00ea01d5d630$b18d4f20$14a7ed60$@gmx.de> <8cad7201fae0b5e421e6a064e93ff21614a42e31.camel@schlueters.de> Date: Fri, 7 Feb 2020 21:48:39 +0100 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:52.0) Gecko/20100101 Firefox/52.0 SeaMonkey/2.49.2 MIME-Version: 1.0 In-Reply-To: <8cad7201fae0b5e421e6a064e93ff21614a42e31.camel@schlueters.de> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Posted-By: 46.59.72.204 Subject: Re: [PHP-DEV] Operator overloading for userspace objects From: ajf@ajf.me (Andrea Faulds) Message-ID: Hi Johannes, Thank you for your points! I think you point out some overlooked issues. Johannes Schlüter wrote: > 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; > } I wonder if it would be a good idea, if we do want operator overloading in PHP, to implement a similar mechanism for this. Perhaps type-specific overloads could be registered via some special function call or declaration, something vaguely like: class Vector { public function __construct() { php\register_overload($this, Matrix::class, '*', function ($a, $b) { /* multiplication implementation here */ }); } } The engine could then do type-matching for you, and would implement commutativity for you if the operator is commutative, so `$someVector * $someMatrix` would call the above function, but so would `$someMatrix * $someVector`. (Note: to support matrix multiplication, I guess commutativity must be overridable. Also, I have forgotten whether multiplying a matrix and a vector is commutative or not :p) I think this approach would be less messy than having to implement full type matching on both sides of a type pair, for a number of reasons: * Instead of Vector having to have an implementation of __mul which checks for Matrix, and Matrix having to have an implementation of __mul which checks for Vector, just one of these types can call register_overload with a single implementation (because the operation is commutative). * Whether two types can be used with a particular operator is clear: either there is such a pair registered, or there is not, and PHP can give appropriate error messages. It is unlikely there will be an issue where one side has __mul but it just throws an exception or somethig. Also, in a case like `$a * $b`, $a can implement support for $b without $b having to support $a, while at the same time $b can implement support for some unrelated other type, without `$b * $a` not working (with the current proposal, imagine $a's __mul handler supporting $b but not vice-versa). * The engine can see conflicts (one class declares an overload involving the other class, and vice-versa) and warn about them, rather than `$a * $b` silently having completely different behaviour to `$b * $a`. This is not to say we necessarily should implement this, but it may be worth thinking about… Thanks, Andrea