Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108300 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 79331 invoked from network); 29 Jan 2020 01:04:00 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 29 Jan 2020 01:04:00 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CA45A1804F2 for ; Tue, 28 Jan 2020 15:14:31 -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=-0.7 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,FREEMAIL_FROM,HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS8560 212.227.0.0/16 X-Spam-Virus: No X-Envelope-From: Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 28 Jan 2020 15:14:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1580253270; bh=J1sSGYOzUUotpfkaAbsJ1KH5rl5Zry+YsrTxX/rCXbY=; h=X-UI-Sender-Class:From:To:Subject:Date; b=A3Z3LenIjPpTBwCIBRJsCwhB/aIUWvDmAhq+fzQnCtfVMHpS5l+76I3OgyadbXlMI gyGxhpZYvCYeoDyRq+sjxqrcsl6oVNzhPve3wYBUqBzCRUklAVPMq1/hBRDO6VnBOn Fncqpr41u1HKhw02rXfQjwvSIc+ne7gFvICTjPks= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from DESKTOPF2PTDOD ([141.35.40.65]) by mail.gmx.com (mrgmx105 [212.227.17.168]) with ESMTPSA (Nemesis) id 1N95eJ-1jbxLN0r0Z-0165jH for ; Wed, 29 Jan 2020 00:14:30 +0100 To: Date: Wed, 29 Jan 2020 00:14:29 +0100 Message-ID: <00ea01d5d630$b18d4f20$14a7ed60$@gmx.de> MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_NextPart_000_00EB_01D5D639.1352C890" X-Mailer: Microsoft Outlook 16.0 Thread-Index: AdXWMEh+LGCw0B+ZQWmoEuoIusxSGw== Content-Language: de X-Provags-ID: V03:K1:iWu6TiMtEg1rvetGO+lVCld0nbvtWKvgMHBpa22HMuDNkHG3l0G FjurYGGPPLlZnBIlr8dQFHaPmn8W3yf3KtOz2ftW3yaLgc7e3MuSVoWhbU1Y+IcgVH7PQQU 5mUp3Z62moaTIZienaLPKYi5oXhhotIOTW+y+kF8j4eWgaLIOtMIwGz/rrK78BMzREYjt8U gicsZ+8wVVBcq7e9YRfkA== X-UI-Out-Filterresults: notjunk:1;V03:K0:HZ1hBEhVeX8=:e/gkBBe4j+ukJBWYNxajwQ TMGHUbhwMJdDYJhGOYqRU/rZSp35r9eO+L4bSLjOZsZoOYl2cKYkFsWUd4oxn2CkyQe3D1QpC TwYj4G5asxsgNiBg2iS8Kg4dAQXiF9JtxgeeA/8KUm5TYWlENICFYBbRk4QB8Ads+WXuhbEBz qcfazPVYy8cBbh2FqdA9PgKHjo+YYpiARur6zgXcG8ZdOHq5ZtWrO0s+ilWqbpVsw1Xzi+J+B 4ZTLwAK7hggnhkL3z5C/uwsaHdwmvFT98P4JUAAB2/5rm8482JlaDmc1USbghFnQvW2X1pQbI uORzK+GQmE0Lk84oC9q4acBjDxZ8EfAjPfHsQ7iEqpyWpuJ+ZIeamjwOWfLnUDdzu1UhCsP55 GZIq2zzMRdS8OarpS9fpo3FspQeRybsUog+AHGLq306IE0e86rhhMtMqF+LX/ln6TGQpRIoL4 I2ayVsbNBQ5btzYxGOLnQ9Rnd9AlmfUTUTVI/3hnoswzySKLvoxyzCcbWLk1hejakSA1Sh4TF lsdw4G7Yty8BizKOLcYN/aGFnNiGG0uUOFHakxGNLPV78yQ3FYcbsNEgz/iXJjFxCabitjwTe i1rNVnTWCxZIMq64siZh76J7rJO5lk4ZPUuPPHcq4B+JAHUTcw8F3PzPO54lZXI1YshKIL4uX CPG2OCehcvDpiE2+SsiEup2LRTjterhxQ9qQyfz/Y+G6qYf9XVgmhgi3CFl3+A3DKs6PsDSYs D/5aSUMLni+S2mvtA17dFmaS8LyrNa9ct7hWzPJ3byF3C6wmNyvt27mqEM2ZNsakCSRTTp9iw me37YzmD4/5ElMo2IbFJ6aOExYxun4293Ow4x3Y7r4yRHkHqPL2H57TLkGz1ixf5ri1Ex7s3q gvWuNbA9UBxsrbkfZolyrzVlSqoypwZOvkNFfBl33lLiXWl1GsAf1bpugJbUlvt9Jfm8eGl3e IttfIG1ngasm/7Qg7e5+zbjzlbVaFxIwykNekn/jRuwCCt7/t97i30cueXwUJzvJXfcPDAQPJ OjVVQuvGKNcpxnyo7lNiyskpyBLHVjx7Vk34NVSGfrHJ68gDBuZpuuPHnCNsEyQGjWxl3+zdW Vt7GWxH+trKiSBhnyFtWRWtosOW4tMgL3ym8G3Oj4ie3RknJKBXOaq5jKuZRN7k9yhKYyQeh/ Jd2IOGwUfcnxKVc7Lxpwo5x0iuUwuZVhMMDwpddbA5sVQlQMftfpY60O28P2huOAAR9xM1Taa K1fPECXS0fJ09BruZ Subject: Operator overloading for userspace objects From: jan.h.boehmer@gmx.de ------=_NextPart_000_00EB_01D5D639.1352C890 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Hello everybody, =20 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)) =20 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. =20 I have tried to implement a proof of concept of the RFC, I encountered = some problems, when implementing the operator functions as (non-static) class members and pass them only the =93other=94 argument: What happens 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: =20 Class X { public static function __add($lhs, $rhs) { //... } } =20 The class function can so decide what to do, based on both operands (so = it 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. =20 I have taken over the idea of defining a magic function for each = operation (like Python does), because I think that way it is the clearest way to = see, 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 = discussion 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). = This would lead to either that only parts of the interfaces are implemented = (and the other just throw exceptions) or that the interfaces contain only one = or two functions (so we would have many interfaces instead of magic = functions in the end). =20 On the topic which operators should be overloadable: My = PoC-implementation 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 = evaluated. 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=92t think this would restrict real world applications much (if you = have an example, where a separate definition of < and >=3D could be useful, = please comment it). Unlike the original idea, I don=92t think it should be possible to = overwrite identity operator (=3D=3D=3D), because it should always be possible to = check 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 = just an abbreviation of $=3D$a+1, so this logic should apply to overloaded = operators as well (in C# it is implemented like this). On the other hand it could = be useful to differentiate between the two cases, so you can mutate the = object 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=92t think that this would be a big problem, so = my PoC-Code does not provide a possibility to override the short hand operators.) For the increment/decrement operators ($a++) it is similar, = it would be nice if it would be possible to overload this operator but on = the other hand the use cases of this operator is really limited besides = integer incrementation and if you want to trigger something more complex, you = should call a method, to make clear of your intent. =20 On the topic in which order the operators should be executed: Besides = the normal priority (defined by PHP), my code checks if the element on the = left 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 = class 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=92t have an idea = how useful this feature would be. =20 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 =20 I would be happy to hear some opinions for this concept, and the idea of overloadable operators in PHP in general. =20 Thanks and Best regards, Jan B=F6hmer =20 ------=_NextPart_000_00EB_01D5D639.1352C890--