Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108301 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 82875 invoked from network); 29 Jan 2020 01:23:09 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 29 Jan 2020 01:23:09 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B25861804F6 for ; Tue, 28 Jan 2020 15:33:37 -0800 (PST) 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-oi1-f196.google.com (mail-oi1-f196.google.com [209.85.167.196]) (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 ; Tue, 28 Jan 2020 15:33:34 -0800 (PST) Received: by mail-oi1-f196.google.com with SMTP id b18so8238747oie.2 for ; Tue, 28 Jan 2020 15:33:34 -0800 (PST) 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=KTczKJFqfRSkhSpS7TTxXWNih4I9NLILFLoFqNxmeDw=; b=OH5xNUhA9UXKzInyO6MUo8pf69htI7IJx3pdXgcjYAj3QFqmPMsUCRswtmO9iZdZfN agBiZI5W4AsBSo0x+wkswl1w8R5TCFWwZfpYKVk7CnsejwPVi0B3QePMWLsier+4hX7o n7HmeqsWRfCH/8tg7CnkMWh/Lcj/VHPO+iNqo+kvlwD77GwYWBRen1dWWMoJMCo+uqz1 6puEhp1CsdE9pf1FEFtG1QHogVSJgxoDnEomP98Yo0nbXhZYU5Gok3oM+CBBgbTRqnfs mBghWcoxPjKpKl/jvxOH8VveAEpSaL/wexmQj05kawcCVogn8pthcU2HzrtDkFzQoRwj /hBA== 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=KTczKJFqfRSkhSpS7TTxXWNih4I9NLILFLoFqNxmeDw=; b=eUauAtUNeGA6y7YqcB/W1vxxeRmvdDOkC9Rw6KpRjDmDjwdT+Pp7DTOkmi5as/qpoY h1I8xBubrh/w4lfN1/YebosCZf88HXV3QzRo106reoDP6/EoK6j+2hkAFpOM3jjNlT7F zlawpiRC0KQuL+nmVl+dtspbJRJju+Dwj3uVBBUpWaNVcu1z4+AKTJIQWRGPxTEzSg+i ovvjVFXALrPc8NhxrgxvxIz4bThcb4F4vzyCiQdqgM07pk6i2Fct+bXjr+U9aY59j36V CE/lcZLbIfNxjBwGfGvoLUuxuQ4tmd1sYnP+mKpqbI6B+WPzwIP8l8sPGZDR1Z0ZMOTi kt+g== X-Gm-Message-State: APjAAAUNwek9g7wC+CvANaUwEcvCXHjlLA4gIiEvR7p5kWJSC3pGJLSo gC/WmjGBb6OeH6gf251AKbX07vtpfjfJ0dZTlt4= X-Google-Smtp-Source: APXvYqwPIjwEKbg24tnBhOYEykad08Ld1OmlrU8d36IonQ7EM4Qv5AT3kYzbqWmYzOlYHakHUc2wzR2Qm9m9/KV82r4= X-Received: by 2002:aca:b1d5:: with SMTP id a204mr4490407oif.82.1580254413565; Tue, 28 Jan 2020 15:33:33 -0800 (PST) MIME-Version: 1.0 References: <00ea01d5d630$b18d4f20$14a7ed60$@gmx.de> In-Reply-To: <00ea01d5d630$b18d4f20$14a7ed60$@gmx.de> Date: Tue, 28 Jan 2020 20:33:21 -0300 Message-ID: To: jan.h.boehmer@gmx.de Cc: PHP Internals Content-Type: multipart/alternative; boundary="000000000000f9b276059d3ba6f8" Subject: Re: [PHP-DEV] Operator overloading for userspace objects From: david.proweb@gmail.com (David Rodrigues) --000000000000f9b276059d3ba6f8 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I think that the left operand is the "owner", the magic method handler, while the right operand is the argument. So for $object * 5, we will have: // $object instance public function __multiply($number): self { return $this->multiply($number); } But for 5 * $object we will have an error. Call order should be respected now once that will be impossible you have $objectA * $objectB without defines a priority (the main handler). -- Atenciosamente, David Rodrigues Em ter., 28 de jan. de 2020 =C3=A0s 20:14, escreveu: > Hello everybody, > > > > the last days I have experimented a bit with operator overloading in > userspace classes (redefing the meaning of arithmetic operations like +, = -, > *, etc. for your own classes). > > This could be useful for different libraries which implements custom > arithmetic objects (like money values, tensors, etc.) or things like > Symfony > string component (concatenate) operator, because it improves readability > much: > > $x * ($a + $b) instead of $x->multiply($a->add($b)) > > > > 4 years ago, there was a RFC about this topic ( > > https://wiki.php.net/rfc/operator-overloading), which was discussed a bit > ( > https://externals.io/message/89967), > but there was no real Outcome. > > > > I have tried to implement a proof of concept of the RFC, I encountered so= me > problems, when implementing the operator functions as (non-static) class > members and pass them only the =E2=80=9Cother=E2=80=9D argument: What hap= pens when we > encounter an expression like 2/$a and how can the class differ this from > $a/2. Also not every operation on every structure is e.g on commutative > (e.g. for matrices A*B =3D/=3D B*A). So I tried a C#-like approach, where= the > operator implementations are static functions in the class, and both > arguments are passed. In my PHP implementation this would look something > like this: > > > > Class X { > > public static function __add($lhs, $rhs) { > > //... > > } > > } > > > > The class function can so decide what to do, based on both operands (so i= t > can decide if the developer wrote 2/$a or $a/2). Also that way an > implementor can not return $this by accident, which could lead to > unintended > side effect, if the result of the operation is somehow mutated. > > > > I have taken over the idea of defining a magic function for each operatio= n > (like Python does), because I think that way it is the clearest way to se= e, > what operators a class implements (could be useful for static analysis). > The > downside to this approach is that this increases the number of magic > functions highly (my PoC-code defines 13 additional magic functions, and > the > unary operators are missing yet), so some people in the original discussi= on > suggest to define a single (magic) function, where the operator is passed= , > and the user code decides, what to do. Advantageous is very extensible > (with > the right parser implementation, you could even define your own new > operators), with the cost that this method will become very complex for > data > structures which use multiple operators (large if-else or switch > constructions, which delegate the logic to the appropriate functions). An > other idea mentioned was to extract interfaces with common functionality > (like Arithmetically, Comparable, etc.) like done with the ArrayAccess or > Countable interfaces. The problem that I see here, is that this approach = is > rather unflexible and it would be difficult to extract really universal > interfaces (e.g. vectors does not need a division (/) operation, but the > concatenation . could be really useful for implementing dot product). Thi= s > would lead to either that only parts of the interfaces are implemented (a= nd > the other just throw exceptions) or that the interfaces contain only one = or > two functions (so we would have many interfaces instead of magic function= s > in the end). > > > > On the topic which operators should be overloadable: My PoC-implementatio= n > has magic functions for the arithmetic operators (+, -, *, /, %, **), > string > concatenation (.), and bit operations (>>, <<, &, |, ^). Comparison and > equality checks are implement using a common __compare() function, which > acts like an overload of the spaceship operator. Based if -1, 0 or +1 is > returned by the comparison operators (<, >, <=3D, >=3D, =3D=3D) are eval= uated. I > think this way we can enforce, that the assumed standard logic (e.g > !($a<$b)=3D($a>=3D$b) and ($a<$b)=3D($b>$a)) of comparison is implemented= . Also I > don=E2=80=99t think this would restrict real world applications much (if = you have > an > example, where a separate definition of < and >=3D could be useful, pleas= e > comment it). > > Unlike the original idea, I don=E2=80=99t think it should be possible to = overwrite > identity operator (=3D=3D=3D), because it should always be possible to ch= eck if > two objects are really identical (also every case should be coverable by > equality). The same applies to the logic operators (!, ||, &&), I think > they > should always work like intended (other languages like Python and C# > handles > it that way too). > > For the shorthand assignment operators like +=3D, -=3D the situation is a= bit > more complicated: On the one hand the user has learned that $a+=3D1 is ju= st > an > abbreviation of $=3D$a+1, so this logic should apply to overloaded operat= ors > as well (in C# it is implemented like this). On the other hand it could b= e > useful to differentiate between the two cases, so you can mutate the obje= ct > itself (in the +=3D case) instead of returning a new object instance (the > class cannot know it is assigned to its own reference, when $a + 1 is > called). Personally I don=E2=80=99t think that this would be a big proble= m, so my > PoC-Code does not provide a possibility to override the short hand > operators.) For the increment/decrement operators ($a++) it is similar, i= t > would be nice if it would be possible to overload this operator but on th= e > other hand the use cases of this operator is really limited besides integ= er > incrementation and if you want to trigger something more complex, you > should > call a method, to make clear of your intent. > > > > On the topic in which order the operators should be executed: Besides th= e > normal priority (defined by PHP), my code checks if the element on the le= ft > side is an object and tries to call the appropriate magic function on it. > If > this is not possible the same is done for the right argument. This should > cover the most of the use cases, except some cases: Consider a expression > like $a / $b, where $a and $b has different classes (class A + class B). = If > class B knows how to divide class A, but class A does not know about clas= s > B, we encounter a problem when evaluating just from left to right (and > check > if the magic method exists). A solution for that would be that object $a > can > express that he does not know how to handle class B (e.g. by returning > null, > or throwing a special exception) and PHP can call the handler on object $= b. > I'm not sure how common this problem would be, so I don=E2=80=99t have an= idea how > useful this feature would be. > > > > My proof-of-concept implementation can be found here: > > https://github.com/jbtronics/php-src > > Here you can find some basic demo code using it: > > https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6 > > > > I would be happy to hear some opinions for this concept, and the idea of > overloadable operators in PHP in general. > > > > Thanks and Best regards, > > Jan B=C3=B6hmer > > > > --000000000000f9b276059d3ba6f8--