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

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to