On Sat, Mar 16, 2019, at 4:43 AM, Rowan Collins wrote:
> On 16 March 2019 05:18:55 GMT+00:00, Larry Garfield 
> <[email protected]> wrote:
> >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.
> 
> 
> Hi Larry,
> 
> That's a great summary, and I agree that structs make most sense viewed 
> as an object with special behaviours, rather than a completely separate 
> type.
> 
> Native support for "evolvable" objects (withFoo methods) would be 
> interesting, although I'm not sure what it would look like - ideally, 
> it would reduce both the boilerplate of declaring them, and the 
> overhead of creating intermediate objects when evolving more than one 
> property. It's still more verbose to write than direct assignment to a 
> property, but no more so than calling a setter method explicitly.

Ruminating on this a while, I'm not sure there's any good way to do it 
until/unless we have property accessor methods.

With a value object, you very often do want/need to maintain some internal 
validity.  There's ample cases where only allowing mutation/evolution (sounds 
like the Zerg from Starcraft) through a method is a requirement, so any 
approach to first class value objects will need to allow for that.

Without adding special syntax, with*() methods already do that well enough that 
there's little need for further syntax help.  At best we could automate the 
$that = clone($this) first line of such methods but that's not really worth the 
effort.

The only reasonable way I see to have both with*()-style method support and 
value-object behavior would be something like this:

$obj2 = $obj1{foo: 'bar'};

Where $obj2 is now a totally new object (give or take internal CoW behavior 
that is just an optimization) where the foo property is now set to 'bar'; 
however, if there is a setter method defined for foo then that gets called; 
which means the setter must be public (which raises other issues), and can do 
whatever additional logic or validation is needed.

So the following two classes would have essentially the same semantic effect:

class Stuff {
   protected $foo;

   public function withFoo($val) : Stuff
   {
     assert($val > 10);
     $new = clone($this);
     $new->foo = $val;
     return $new;
   }
}

$s = new Stuff();
$s2 = $s->withFoo('bar');

struct Stuff {
   protected $foo
   public set($val) {
     assert($val > 10);
     $this->foo = $val;
    }
}

$s = new Stuff{};
$s2 = $s{foo: 'bar'};

Whether that's a good approach or not I don't now, but it's the only one that I 
could see working.  And just looking at the sample code above I see a lot of 
potential for very bad things.

> One thing that hasn't been brought up yet is identity: if structs are 
> just a special kind of object, you would be able to compare them by 
> identity; if they are more like arrays, you would only be able to 
> compare them by value. I think not having identity would make sense, 
> both because of the expected use cases, and because it plays better 
> with copy-on-write optimisations:
> 
> $x = new SomeStruct { foo=0 };
> $x = $y; // copy-by-value, but optimised internally to copy-on-write
> var_dump($x === $y); // true? at this point, there is no new object 
> identity
> $y->foo = 1; // copy-on-write triggers internally
> var_dump($x === $y); // false? they are now separated
> $y->foo = 0;
> var_dump($x === $y); // false? structs are now identical again, but 
> separated in memory
> 
> This is much more intuitive if structs have no identity, and any two 
> instances with the same values are considered identical.

I agree you'd expect value structs/objects to equate like primitives, not like 
pointers.

Perhaps this is an argument for an __equals() method?  Or the 
previously-rejected comparison magic method RFC?

--Larry Garfield

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

Reply via email to