Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:90066 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 13727 invoked from network); 5 Jan 2016 05:22:14 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 5 Jan 2016 05:22:14 -0000 Authentication-Results: pb1.pair.com smtp.mail=larry@garfieldtech.com; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=larry@garfieldtech.com; sender-id=unknown Received-SPF: error (pb1.pair.com: domain garfieldtech.com from 66.111.4.28 cause and error) X-PHP-List-Original-Sender: larry@garfieldtech.com X-Host-Fingerprint: 66.111.4.28 out4-smtp.messagingengine.com Received: from [66.111.4.28] ([66.111.4.28:41037] helo=out4-smtp.messagingengine.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id F8/A4-24214-4035B865 for ; Tue, 05 Jan 2016 00:22:13 -0500 Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 02DC52085D for ; Tue, 5 Jan 2016 00:22:09 -0500 (EST) Received: from frontend1 ([10.202.2.160]) by compute2.internal (MEProxy); Tue, 05 Jan 2016 00:22:10 -0500 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:references :subject:to:x-sasl-enc:x-sasl-enc; s=smtpout; bh=d694ijJ2lUrh5bi NZZaEXTWgy/8=; b=o4r8AB3cH7VXrt28DcfnTe8+QJRI4+iiRRRSI5IAAQ7uaaY zAQ7cKvgx5e45vtOJRgH1Njsaa8H4CTYl9lwsVt9BljRQzy3O/g2eY0CuaPnK2wF +ViTf0OMEoLwNQeVyO8W/LD5V84BXwBsARpAmZlT7J1oJL3za1L4nrtETe3k= X-Sasl-enc: 2+CsnjDdOV67BdynbB6K719PlOhuCFbSoIlJEpUczuE4 1451971329 Received: from [192.168.42.5] (c-50-178-40-84.hsd1.il.comcast.net [50.178.40.84]) by mail.messagingengine.com (Postfix) with ESMTPA id 96C5EC016F2 for ; Tue, 5 Jan 2016 00:22:09 -0500 (EST) To: internals@lists.php.net References: <12.34.07292.41F9A865@pb1.pair.com> <568AF408.6020105@gmail.com> <68.B0.07292.98AFA865@pb1.pair.com> Message-ID: <568B52C2.3090301@garfieldtech.com> Date: Mon, 4 Jan 2016 23:21:06 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.4.0 MIME-Version: 1.0 In-Reply-To: <68.B0.07292.98AFA865@pb1.pair.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] Re: RFC Operator Overloading in Userspace From: larry@garfieldtech.com (Larry Garfield) On 01/04/2016 05:04 PM, Andrea Faulds wrote: > I agree that we could do something with interfaces. I would like to > point out that we actually already have an example of this, in the > form of the \ArrayAccess interface, which requires you to implement > all the different indexing operations at once. Unfortunately, though, > \ArrayAccess doesn't give us a precedent for dealing with the $this > issue (in `$a + $b`, who gets called, how do we handle differing > types, etc?), but it's a start. As a non-Haskell-knowing dev I only followed a portion of the conversation leading up to this, but I think I'm tentatively liking the direction this is moving. If I understand correctly, the suggestion is to add, eventually, e.g.: interface Comparable { public function compare($other) : int; (returns -1, 0, 1) } interface ArithmeticStuff { public function add($other) : ArithmeticStuff; public function subtract($other) : ArithmeticStuff; // ... } Am I following? If so, I see the following (possibly surmountable) concerns: 1) The ArithmeticStuff interface (obviously better named) still has no way to prevent mutation of $this or $other. I still see that as user-space trainwreck waiting to happen unless there's some way to force those methods to NOT modify $this in any way. 2) Besides the issue with different classes that both implement the same operator interface, as Andrea notes, it also would cause issues with subclasses. To wit: class Money implements Numberish { protected $valueWithoutDecimals; public function add($other) { $ret = new Money($this->valueWithoutDecimals); $ret->valueWIthoutDecimals += $other->valueWithoutDecmials; return $ret; } } class Dollars extends Money {} $d1 = new Dollars(4); $d2 = new Dollars(5); $d3 = $d1 + $d2; $d3 would now be an instance of Money, NOT Dollars. Sure, there are better ways of writing that implementation that would avoid that problem (cloning $this, using get_called_class(), etc.) but that the potential is there is worrisome. Possible solution to 1 and 2: Instead of just passing $other, pass $clone and $other, and block $this. To wit: class Money implements Numberish { protected $valueWithoutDecimals; // Being static, $this doesn't exist although it's what $clone was cloned from. public static function add($clone, $other) { $clone->valueWIthoutDecimals += $other->valueWithoutDecmials; return $clone; } } That way, the engine pre-clones $this for me, and I operate from that. I could ignore $clone if I wanted to, but I cannot ever modify $this. (That wouldn't address the mixed class question entirely, but does address forced-immutability.) 3) A long-standing source of aggravation for user-space devs is that it's impossible to build an object that is a for-reals replacement for arrays. We can implement ArrayAccess, Countable, Iterator, whatever, but for all practical purposes they're still not even remotely close to being arrays as far as the type system or array functions are concerned. If what we're talking about here is adding more "act like a primitive" interfaces for objects, we need to think through all of those little nuances to make them as non-annoying as possible. (Eg, would an integer pass a Numberish type check? In weak mode or strict mode? Can I pass a Numberish to strpos()? Can I pass a Stringish to strpos()?) I don't have answers for those but I would hope we'd think those through to avoid the split existence that is collections/arrays. > >>> should work on all number types. I don't think GMP currently overloads >>> any of these, though, and there is the possibility we might create >>> confusion if overloading extended to math functions. >> >> Which reinforces the point above that maybe before the are tackling the >> operators in userspace we need to identify use cases and create >> structure for them, and then it might be easier for us to avoid >> inconsistencies. We're kind of moved forward with GMP without doing >> that, but fortunately it's easier to fix such things while it's confined >> to the engine/extensions. Once it's in userspace, changing it would be >> way harder. > > In full agreement there. On that note, it's of course possible to > support overloading certain things, but only internally. It might be > reasonable to expose __assign_add (well, its C equivalent) to GMP but > not to userland, for instance. > > Regarding use cases, there are a few that come to mind. > > One of these is userland number types. We don't, to the best of my > knowledge, currently have a decimal type as a PHP extension, so > applications which need it must look to userland implementations > (often backed by bcmath underneath). These would be more pleasant and > intuitive to use if they could use our normal arithmetic operators. > For such userland types, I imagine an interface with basic arithmetic > operations would work. Perhaps we might call it \Number. A question > might be whether to split apart integer and float operations like > Haskell does. I'm not sure what fits this use-case best, though it's > probably better not to require classes to implement what they don't > need, and just let people get an error if they do, say, ($decimal % 5). > > Another use case is userland string types. There's several projects > out there which wrap PHP strings in an object to provide an > object-oriented interface which is more pleasant to use than PHP's > string API. There is also the use-case of Unicode string wrappers, > since PHP still doesn't have a Unicode string type. They would benefit > from being able to overload the concatenation operator, and possibly > also the binary operations (^, & and |) which work on strings. > Everything else these types need is already overloadable with the > existing \ArrayAccess and \Iterator(Aggregate). There is a problem > here, through, in what we should require such classes to implement > beyond concatenation. Requiring nothing else leaves it open to abuse > (I can already imagine `.` being used for the dot product), but > requiring, say, binary operations would be unfair (they're nonsense in > the context of Unicode), as would \ArrayAccess (Unicode strings may > have non-constant time indexing) or \Traversable (a Unicode string > class might choose to lack a default iterator to force the user to > consider the different ways a Unicode string can be used). Maybe we > could require \Countable, but even that has problems regarding > Unicode, non-constant time complexity especially. So, I'm not quite > sure what to do there. > > Thanks. An example of a user-space string object is in Drupal 8: We added (very late) a TranslatableString class, which contains a string template (with placeholders), the placeholder replacements, and various cache-related metadata. Off hand, I don't know if adding a Stringish interface to that would be good or terrible. :-) It needs to be compiled into an actual string (replacing the template with an alternate version for another language if appropriate) during rendering so that we have the right cache information available, a process that requires external services. Off hand I have no idea what adding a concat() method or position()/ArrayAccess method to that would do or if it would eat our kittens, but I mention it here as another example in the wild. --Larry Garfield