Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:104769 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 40387 invoked from network); 16 Mar 2019 08:28:34 -0000 Received: from unknown (HELO out2-smtp.messagingengine.com) (66.111.4.26) by pb1.pair.com with SMTP; 16 Mar 2019 08:28:34 -0000 Received: from compute7.internal (compute7.nyi.internal [10.202.2.47]) by mailout.nyi.internal (Postfix) with ESMTP id CACC22013C for ; Sat, 16 Mar 2019 01:19:22 -0400 (EDT) Received: from imap26 ([10.202.2.76]) by compute7.internal (MEProxy); Sat, 16 Mar 2019 01:19:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; bh=SgvXOH eAHLZgs/UacPLBxsg0/zHaw5yQ8M4h61h6Wm0=; b=dqDRuao/A2ifdYZbDshsg5 ENSAiwFo5w9Lkcu+A1+oO/UXY/yAQLRU1N+53N/XwDphHIvz0C/wOp5knBFEe7tO G0H76oyYEAgMhny6CRIGDdyTkmOMdwO9iR7R23/N8reLJCi/lyLfbTu47+GPgCEX 6/SoMHfpJqybSOvsRZ7ZkVCMr9LgDmYOoAm8pKoNbMlG78CV5pOUL1Kd56iVl8H4 ziVzyU3GgWZnznhMEpkHjzIrl09tYRlIMi5KzHpukNoBJglsnSb5MbGjc+YgjAEx J5ZBZn9am4Ke8da2Zj17HqhZeLx9xqJV7W+Y/sJR8gkJhDN693QsIq3tcbqGz4yg == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedutddrheeigdeludcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkjghffffhvffutgesthdtre dtreertdenucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhies ghgrrhhfihgvlhguthgvtghhrdgtohhmqeenucffohhmrghinhepghhithhhuhgsrdgtoh hmnecurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehgrghrfhhivghlughtvggt hhdrtghomhenucevlhhushhtvghrufhiiigvpedt X-ME-Proxy: Received: by mailuser.nyi.internal (Postfix, from userid 501) id E6A7DB4641; Sat, 16 Mar 2019 01:19:21 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.1.5-976-g376b1f3-fmstable-20190314v3 Mime-Version: 1.0 X-Me-Personality: 10727885 Message-ID: <644326f6-ef68-4442-b35f-b9114cad4a5b@www.fastmail.com> In-Reply-To: References: Date: Sat, 16 Mar 2019 01:18:55 -0400 To: internals@lists.php.net Content-Type: text/plain Subject: Re: [PHP-DEV] Re: [Proposal] Struct Data Types From: larry@garfieldtech.com ("Larry Garfield") On Fri, Mar 15, 2019, at 10:56 PM, Kenneth Ellis McCall wrote: > Kenneth Ellis McCall wrote: > > Hey all, > > > > I'm looking to get feedback on a RFC I want to propose. > > > > PHP RFC: Addition of the 'struct' data type. > > > > Hey all, > > Hopefully this addresses the questions you had in someway, even if it's > not a direct answer and might propose other things. Also sorry for the > brevity. > > Immutability: > My initial thought was to have the property list be immutable, such as > you can't add or remove properties, but could change the value. > Maybe it would be possible to do something like this: > > const Acme\MyStruct $myStruct = { > ... > }; > > To make just the instance of the struct be fully immutable. > > Other options could be another type, say constStruct (say that ten times > fast), or maybe add a const hint type (or readonly) to the properties, like: > > struct Acme\MyStruct { > const int id; > } > > > > Access: > I think I'm leaning towards the arrow accessors. > > > Making it more of class type: > Initial thinking, and why I thought we would need this, is that I would > like a way to not use class objects for data bags (like that you see > with entities), since you can abuse classes (and arrays) like this: > > class xyz { > > }; > > $a = new xyz(); > $a->def = 123; > > // Returns 123. > echo $a->def; > > While people shouldn't do it, but because they can, they will. > > Types hints: > I think having them on the left side would be the best, since it kind of > matches what we already do. > As for the loosely type properties, I'm kind of on the border on that. I > really want to have strongly / statically typed hints, so it could > enforce some better habits. On the other side, for historical > purposes... Would definitely want to take a consensus on this. > > > Validation: > I have another item I want to bring up, but don't think it would go over > well: https://github.com/ellisgl/PHP-RFC-Advanced-Type-Hint-Validors > > > Array / Class features: > When I wrote, 'resembles a mix of a class and an array' I was thinking > loosely around the styling, accessing properties and errors. > > > Copy on Write or pass by reference/value: > I think I'm more with Levi with the "pass by-value with copy-on-write > semantics", of course, since this is still draft mode, it could go > another way after further investigation and testing during implementation. Hi Kenneth. I'm a loud proponent of more formally defined data structures in general, so I'm very much on board in concept. However, *almost* all of what you describe either can be done with classes today, has been proposed to add to classes already even if it hasn't passed yet, or could be added to classes just as easily. I've actually started using public-property classes as essentially this sort of struct recently, and for non-API use I quite like it. 1) As of PHP 7.4, class properties can be typed. 2) Union/compound types have been discussed many times; if they ever get adopted they would no doubt be adopted universally and not specific to just one data structure type. (Viz, they'd work on classes, parameter types, and return types, too.) 3) There was a proposal on list less than a week ago to add a "locked" marker for classes to prevent the addition of properties at runtime. The reception was positive although it's obviously not a guarantee to pass. (I'm in favor.) 4) The "set properties in place of a constructor" approach is totally compatible with classes as well, at least in concept. It could also help with the common "my constructor does nothing but set properties to same-named constructor params" pattern. I'm sure someone would propose adding it to classes as well very shortly after it was added to a new "struct" type. 5) IMO, the previously proposed (~2-3 years ago?) property accessor RFC would be a superior way to handle property-level access control. In short, it would allow for Javascript-esque get/set methods that behave like properties, but you can lock down their type and their public/private/protected; thus a private set() method would effectively render a property read-only to the outside world. IIRC it failed mainly due to performance concerns; (I don't know the engine well enough to suggest a way around them at this point.) But that would effectively allow read-only properties on classes. The one exception is the passing and modification semantics, which are two different things. On the one hand, there's the passing semantics. As others have noted, objects currently pass by handle (which means they feel like they're by reference even though they're technically not), while arrays and everything else pass by value. A pass by value "object" has its advantages, no question, but there are ways around that. For example, PSR-7 request/response objects kicked off the idea of "evolvable" objects; their "mutator" method (with*()) only ever clone-and-return-modified (similar to DateTimeImmutable), so even though they technically pass by handle you don't have to worry about spooky-action-at-a-distance. However, that dovetails into the question of property mutability (modification semantics). Assuming object-like syntax for the moment: struct MyStruct { string $foo; int $count = 5; } $m = new MyStruct{foo: 'bar'}; $m->foo = 'baz'; Is that legal? It's definitely not "immutable" behavior by any standard definition of that term. However, if you don't allow that then you have an object that is truly immutable, which means of fairly little use. You'd need to invent some new syntax for "create a new struct instance that is just like this other one except for..." Off the top of my head I can think of two possibilities: $n = $m{ foo: 'baz'}; $n = $m->foo = 'baz'; In both cases, $n is now foo: 'baz', count: 5. Both feel kind of icky to me, even if the second weren't highly confusing with object syntax. Of course, if that were resolved nicely for structs I give it about 12 seconds after structs are approved before someone asks for the same capabilities for classes. Of course, there's a second problem. I give it about another 14 seconds after structs are approved before someone asks for methods on structs, because even with typed properties you can still very easily run into data inconsistency problems; a string that doesn't match a required phone number format, or a property that should update when some other property does, etc. So we'd need to think about adding methods to structs (which is something that C++, Go, Rust, etc. all support... because structs and objects are the same thing in those languages, more or less). At which point... we basically just have objects with PHP4-style passing semantics. :-) So if the net result is that we eventually end up with "structs are objects that pass funny", it would be far less effort and confusion, IMO, to simply allow for "objects that pass funny" as a first class thing; everything else described here either is already the case for objects or if added to structs would get added to objects sooner or later. I don't know that simply adding a byval keyword to a class definition would be the way to go about it; maybe using the `struct` keyword in place of `class` but otherwise being identical? As others have noted there's ample potential for confusion there when using an object/struct that may not pass as intended. In general, though, I think beefing up objects to be "more usable as structs" (locked against dynamic properties, getter/setter methods, possibly some improved tooling for with*()-style behavior, etc.) is the more sustainable long-term solution than adding an entirely new language construct. --Larry Garfield