# Immutable modifier

- Thread: https://externals.io/message/89223
- Original archive: https://news-web.php.net/php.internals/89223
- Started by Chris Riley on 2015-11-16T09:15:41+00:00
- Messages: 27
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/89223.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #89223 — Chris Riley — 2015-11-16T09:15:41+00:00

URL: https://externals.io/message/89223#89223

Hi,

There has been a lot of interest recently (eg psr-7) in immutable data. I'm
considering putting an RFC together to add language support for immutables:

immutable class Foo {
public $bar;
public function __construct($bar) {
$this->bar = $bar;
}
}

Immutable on a class declaration makes all (maybe only public?) properties
of the class immutable after construct; assigning to a property would
result in a Fatal error.

class Foo {
public $bar;
immutable public $baz;
}

Immutable on a property makes the property immutable once it takes on a
none null value. Attempts to modify the property after this results in a
fatal error.

Any thoughts?
~C

---

## #89224 — Daniel Persson — 2015-11-16T09:24:01+00:00

URL: https://externals.io/message/89223#89224 · In reply to: #89223

Any differance from the final keyword?

http://php.net/manual/en/language.oop5.final.php



> Hi,
>
> There has been a lot of interest recently (eg psr-7) in immutable data. I'm
> considering putting an RFC together to add language support for immutables:
>
> immutable class Foo {
> public $bar;
> public function __construct($bar) {
> $this->bar = $bar;
> }
> }
>
> Immutable on a class declaration makes all (maybe only public?) properties
> of the class immutable after construct; assigning to a property would
> result in a Fatal error.
>
> class Foo {
> public $bar;
> immutable public $baz;
> }
>
> Immutable on a property makes the property immutable once it takes on a
> none null value. Attempts to modify the property after this results in a
> fatal error.
>
> Any thoughts?
> ~C

---

## #89225 — Chris Riley — 2015-11-16T09:29:57+00:00

URL: https://externals.io/message/89223#89225 · In reply to: #89224

> Any differance from the final keyword?
>
> http://php.net/manual/en/language.oop5.final.php
>
> 
>
> > […]
>
>
Yes, final prevents child classes from overriding a method or from
extending a class entirely, this is a compile time constraint. Immutable
prevents data modifications and as such is a run time constraint.

~C

---

## #89226 — Lorenzo Fontana — 2015-11-16T09:33:35+00:00

URL: https://externals.io/message/89223#89226 · In reply to: #89224

I really like the concept of immutability, but I think that it should be
applicable at  instance level rather than declaration.

I would also prefer another keyword than immutable.

Final does not make the properties immutable, it makes the class not
extensible.


> Any differance from the final keyword?
>
> http://php.net/manual/en/language.oop5.final.php
>
> 
>
> > […]
> I'm
> > […]
> immutables:
> > […]
> properties
> > […]

---

## #89227 — Chris Riley — 2015-11-16T09:40:08+00:00

URL: https://externals.io/message/89223#89227 · In reply to: #89226

On 16 November 2015 at 09:33, Lorenzo Fontana <fontanalorenz@gmail.com>
wrote:

> I really like the concept of immutability, but I think that it should be
> applicable at  instance level rather than declaration.
>
> I would also prefer another keyword than immutable.
>
> Final does not make the properties immutable, it makes the class not
> extensible.
> 
>
> > […]
>
What instance level syntax would you propose?

$foo = new immutable bar();  ?

or

immutable $foo = new bar();

Gives less flexibility imo and less guarantees of correctness.

Consider the current user land implementation of immutable classes:

class Foo {
private $bar;
public function __construct($bar) {
$this->bar = $bar;
}

public function getBar() {
return $this->bar;
}
}

Is already done at declaration declaration based immutability is probably
more desirable.

What keyword would you suggest other than immutable?

~C

---

## #89229 — Mathieu Rochette — 2015-11-16T10:00:49+00:00

URL: https://externals.io/message/89223#89229 · In reply to: #89226

hi,


> I really like the concept of immutability, but I think that it should be
> applicable at  instance level rather than declaration.
I'm not sure about that, most PHP code assume mutable objects, I doubt 
much existing classes would be usable as immutable
>
> I would also prefer another keyword than immutable.
>
> Final does not make the properties immutable, it makes the class not
> extensible.
> 
>
> > […]
I don't think it's enough to make only public properties immutable. this 
would lead to a lot of mutable "immutable" classes, eg:

immutable class my_class {
   private $my_prop;
   __construct($my_prop) {
     $this->my_prop = $my_prop;
   }
   public function getMyProp() {
     return $this->my_prop;
   }
   public function setMyProp($my_prop) {
     return $this->my_prop = $my_prop;
   }
}

how is this class immutable ?
also, what about __isset/__set ? Would it be allowed to assign non 
immutable types to properties of an immutable class ?
> > […]

---

## #89231 — Lorenzo Fontana — 2015-11-16T11:13:25+00:00

URL: https://externals.io/message/89223#89231 · In reply to: #89227

2015-11-16 10:40 GMT+01:00 Chris Riley <t.carnage@gmail.com>:

>
>
> On 16 November 2015 at 09:33, Lorenzo Fontana <fontanalorenz@gmail.com>
> wrote:
>
> > […]
> What instance level syntax would you propose?
>
> $foo = new immutable bar();  ?
>
> or
>
> immutable $foo = new bar();
>
> Gives less flexibility imo and less guarantees of correctness.
>
> Consider the current user land implementation of immutable classes:
>
> class Foo {
> private $bar;
> public function __construct($bar) {
> $this->bar = $bar;
> }
>
> public function getBar() {
> return $this->bar;
> }
> }
>
> Is already done at declaration declaration based immutability is probably
> more desirable.
>
> What keyword would you suggest other than immutable?
>
> ~C
>



I would prefer


immutable $foo = new bar();

that can also be applied to:

immutable $bar = "string";
immutable $baz = 1;
immutable $arr = [1, 2, 3];

and not only to classes.

The "userland implementation of immutable classes" you are proposing is not
immutable at all.
First of all one could define another accessor method that can change the
state or can bind a closure to $this and change an internal property at
runtime.

Some possible keywords that "means" immutable to me are:

let $bar = "string";
val $bar = "string";

What do you think?

---

## #89232 — Rowan Collins — 2015-11-16T11:30:31+00:00

URL: https://externals.io/message/89223#89232 · In reply to: #89229

Mathieu Rochette wrote on 16/11/2015 10:00:
> Would it be allowed to assign non immutable types to properties of an 
> immutable class ?

I think this is the trickiest part of trying to enforce immutability: 
nesting. Consider the following:

// Basic properties
$my_immutable_object->foo = 42;
$my_immutable_object->foo++;

// Nested object - implicitly immutable? required to be explicitly 
immutable?
$my_immutable_object->bar->something = 42;
$my_immutable_object->bar->setSomething(42);

// Array access - no idea how this should work
$my_immutable_object->quux[0] = 42;
$my_immutable_object->quux[] = 'Hello';
$my_immutable_object->quux['World'][0]['X'] = 'A';


The other problem is that to be useful, certain functions usually need 
to be able to mutate the state of objects which are officially 
immutable. For instance, implementations of PSR-7 need a set of copy 
constructors for the withX methods. You could do this using the 
suggested rule that immutability applies only to non-null properties, 
and manually populate every property of an empty instance, but it's much 
more efficient to "clone $this" and then change the one property that 
needs to be different.

For this particular example, you could write quite a nice implementation 
by "freezing" the instance once it's initialised:

// example plucked from 
https://github.com/guzzle/psr7/blob/master/src/Request.php#L101
     public function withMethod($method)
     {
         // The method starts by making a mutable clone of the immutable 
$this
         $new = clone $this;
         $new->method = strtoupper($method);

         # SUGGESTED ADDITION: new keyword to set an object to be immutable
         freeze $new;
         // After this point, nothing should be able to mutate the new 
object, only clone it

         return $new;
     }


The freeze operation could perhaps be recursive, or perhaps it could 
work like "clone", with an "__freeze" method responsible for freezing 
nested objects as necessary. The ability to freeze an array would also 
be useful in that case...

I realise this is a lot more complex than the original suggestion, but I 
think something of this level of detail is needed to make it useful.

Regards,
-- 
Rowan Collins
[IMSoP]

---

## #89247 — Larry Garfield — 2015-11-16T22:11:08+00:00

URL: https://externals.io/message/89223#89247 · In reply to: #89223

> Hi,
>
> There has been a lot of interest recently (eg psr-7) in immutable data. I'm
> considering putting an RFC together to add language support for immutables:
>
> immutable class Foo {
> public $bar;
> public function __construct($bar) {
> $this->bar = $bar;
> }
> }
>
> Immutable on a class declaration makes all (maybe only public?) properties
> of the class immutable after construct; assigning to a property would
> result in a Fatal error.
>
> class Foo {
> public $bar;
> immutable public $baz;
> }
>
> Immutable on a property makes the property immutable once it takes on a
> none null value. Attempts to modify the property after this results in a
> fatal error.
>
> Any thoughts?
> ~C

As a firm believer in the value of immutable data structures, I don't 
believe such a simple approach would be useful and would likely be 
counter-productive.

The trick is there are 2 cases for immutability: An immutable service 
object, and an immutable data object.

A service object should be immutable once it's setup.  However, that 
setup *may*, in some cases, involve setter injection.  That's inferior 
or constructor injection but that is not always feasible. In practice, 
I'm not sure we need new syntax here at all.

For data objects, this is where it gets interesting.  Data objects *do* 
need to be modifiable for a given context... for that context. Immutable 
data objects are, largely, useless and in my experience harmful unless 
they have a ->giveNewVersionWithThisOneChange() method.  In PSR-7, 
that's the with*() methods.  DateTimeImmutable has the same methods as 
DateTime, but they return new instances rather than modifying the 
invoked object (despite their confusing names, which are like that for 
historical reasons).

My ideal would be to revive the property RFC and leverage per-property 
access control separately for read and write to create 
externally-immutable objects.  That would allow for with*() methods to 
be implemented as appropriate for a given object.

The problem with a simple keyword approach is that "modifier" methods 
often should not, and in the case of PSR-7 cannot, correspond to simple 
get/set mapping to an object property.  (See withAddedHeader(), 
withoutHeader(), etc.)  Some level of flexibility needs to be given to 
the implementer to create an effectively-immutable object that is more 
than just a bare struct that's hard to work with.  If there were some 
way to flag for the compiler that "This object is supposed to be 
effectively-immutable, please check and make sure I didn't screw that 
up", that would be lovely but sounds hard. :-)

-- 
--Larry Garfield

---

## #89248 — Mathieu Rochette — 2015-11-16T23:11:24+00:00

URL: https://externals.io/message/89223#89248 · In reply to: #89247

> 
> > […]
>
> As a firm believer in the value of immutable data structures, I don't 
> believe such a simple approach would be useful and would likely be 
> counter-productive.
agreed
>
> The trick is there are 2 cases for immutability: An immutable service 
> object, and an immutable data object.
>
> A service object should be immutable once it's setup.  However, that 
> setup *may*, in some cases, involve setter injection.  That's inferior 
> or constructor injection but that is not always feasible. In practice, 
> I'm not sure we need new syntax here at all.
I'm curious here, why is construction not always enough ? do you have a 
use case in mind ?
>
> For data objects, this is where it gets interesting.  Data objects 
> *do* need to be modifiable for a given context... for that context. 
> Immutable data objects are, largely, useless and in my experience 
> harmful unless they have a ->giveNewVersionWithThisOneChange() 
> method.  In PSR-7, that's the with*() methods.  DateTimeImmutable has 
> the same methods as DateTime, but they return new instances rather 
> than modifying the invoked object (despite their confusing names, 
> which are like that for historical reasons).
How does that makes them mutable in a "given context" ? the with*() can 
always create a new object with everything in the constructor, am I 
missing something ?
>
> My ideal would be to revive the property RFC and leverage per-property 
> access control separately for read and write to create 
> externally-immutable objects.  That would allow for with*() methods to 
> be implemented as appropriate for a given object.
are you talking about this one ? 
https://wiki.php.net/rfc/readonly_properties
>
> The problem with a simple keyword approach is that "modifier" methods 
> often should not, and in the case of PSR-7 cannot, correspond to 
> simple get/set mapping to an object property.  (See withAddedHeader(), 
> withoutHeader(), etc.)  Some level of flexibility needs to be given to 
> the implementer to create an effectively-immutable object that is more 
> than just a bare struct that's hard to work with.  If there were some 
> way to flag for the compiler that "This object is supposed to be 
> effectively-immutable, please check and make sure I didn't screw that 
> up", that would be lovely but sounds hard. :-)
>


-- 
Mathieu Rochette

---

## #89249 — Levi Morrison — 2015-11-16T23:39:57+00:00

URL: https://externals.io/message/89223#89249 · In reply to: #89223

> Hi,
>
> There has been a lot of interest recently (eg psr-7) in immutable data. I'm
> considering putting an RFC together to add language support for immutables:
>
> immutable class Foo {
> public $bar;
> public function __construct($bar) {
> $this->bar = $bar;
> }
> }
>
> Immutable on a class declaration makes all (maybe only public?) properties
> of the class immutable after construct; assigning to a property would
> result in a Fatal error.
>
> class Foo {
> public $bar;
> immutable public $baz;
> }
>
> Immutable on a property makes the property immutable once it takes on a
> none null value. Attempts to modify the property after this results in a
> fatal error.
>
> Any thoughts?
> ~C
>

In my opinion we should just generalize const for this, rather than add a
new keyword.

---

## #89250 — Larry Garfield — 2015-11-16T23:41:53+00:00

URL: https://externals.io/message/89223#89250 · In reply to: #89248

>
> 
> > […]
> agreed
> > […]
> I'm curious here, why is construction not always enough ? do you have 
> a use case in mind ?

There are various cases, like a logger, a container-aware service (such 
as factories), or other cases where you may want a class to opt-in to a 
specific dependency by interface.  In that case your object creation 
process (DI Container or otherwise) can do something like this:

$object = create_object_via_constructor_injection(...);
if ($object instanceof Loggable) {
   $object->setLogger($this->getSystemLogger());
}

There are also cases in, eg, Drupal where forcing constructor injection 
for a class that is extending another results in unnecessary and 
annoying boilerplate.  (I'm thinking of our new Plugin system.)  As I 
said, it's suboptimal but use cases for setter injection (really 
interface injection) do exist, so a language-strict enforcement of 
constructor-or-nothing are not always applicable.

In any event, I don't think service objects really need this feature 
anyway as there's no data on the service to access; if it had any, it 
wouldn't be a service object. :-)

> > […]
> How does that makes them mutable in a "given context" ? the with*() 
> can always create a new object with everything in the constructor, am 
> I missing something ?

The "everything in the constructor" is the problem.  That results in, 
essentially, an obscenely long function call that just happens to be 
named __construct().  If I wanted something that obscure and hard to 
work with I'd just use anonymous arrays. :-)

Consider PSR-7's ServerRequest object.  It's immutable.  However, when 
passing it from one middleware to the next you want to be able to pass a 
"modified" version of it, containing extra properties that indicate the 
result of routing, for example.  Or you have a middleware that does 
cookie encryption, so it needs to "modify" the cookie header.

If that meant each middleware needed to do something like this:

function routing_middleware(ServerRequest $r, callable $next) {
   $attributes = $r->getAttributes();
   $attributes['controller'] = 'foo';

   $new = new ServerRequest($r->getMethod(), $r->getUri(), 
$r->getHeaders(), $r->getBody(), $r->getAttributes());

   return $next($new);
}

Well, you can see how gross that is, and it gets worse as soon as you 
need to modify more than one property. You essentially need to `extract()` 
the object back to primitives and build a new one, which is more 
cumbersome, more verbose, slower, and makes kittens cry.  At this point 
it's easier to just have a big anonymous array and pass that around; 
it's no less self-documenting and easier to work with.

(I had to work with such an object once when doing a Symfony REST 
project, because that's what the HATEOAS bundle did.  It was impossible 
to work with.)

Instead, with the with*() methods, PSR-7 lets you do this:

function routing_middleware(ServerRequest $r, callable $next) {
   $new = $r->withAttribute('controller', 'foo');

   return $next($new);
}

Or even inline that to a single line (if you really do have a hard coded 
controller, which is unlikely but other cases like cookie encryption might).

function cookie_middleware(ServerRequest $r, callable $next) {
   return $next($r->withHeader('Cookie', 
decode_cookie($r->getHeader('Cookie')));
}

Which looks nice and functional, too.

It's much easier to read, much easier to write, still communicates 
intent, and thanks to copy-on-write is no more expensive under usual usage.

Most of the objection to immutable PSR-7 objects, including my own, was 
based on the assumption that we'd be stuck with the former example, 
which is a major PITA.  The development of the with*() methods is what 
made immutable PSR-7 feasible.

In less generic form, imagine if DateTimeImmutable worked like this:

$d = new DateTimeImmutable();
$d2 = new DateTimeImmutable($d->format('c') . ' + 1 day');

Instead of like this:

$d2 = $d->modify('+1 day');

I think you'll agree the second is much nicer.

In all of these cases, though, we get the benefit of locality of 
effect.  Changes to an object in one function cannot impact another 
function, because it's not a change but a new object.

> > […]
> are you talking about this one ? 
> https://wiki.php.net/rfc/readonly_properties

No, I meant this earlier one:

https://wiki.php.net/rfc/propertygetsetsyntax-v1.2

I dislike the readonly keyword for basically all the same reasons I gave 
earlier in this thread. :-)


-- 
--Larry Garfield

---

## #89251 — Sebastian Bergmann — 2015-11-17T00:04:22+00:00

URL: https://externals.io/message/89223#89251 · In reply to: #89223

> Any thoughts?

  Would love to have it for implementing value objects.

---

## #89253 — Andrea Faulds — 2015-11-17T01:47:06+00:00

URL: https://externals.io/message/89223#89253 · In reply to: #89250

Hi Larry,

Larry Garfield wrote:
> The "everything in the constructor" is the problem.  That results in,
> essentially, an obscenely long function call that just happens to be
> named __construct().  If I wanted something that obscure and hard to
> work with I'd just use anonymous arrays. :-)

Huh?

"with" methods and __construct are not exclusive. In fact, you could 
have a private constructor and only have "with" methods. Only allowing 
assignment to properties in the constructor doesn't prevent having a 
nice API: have your methods call the constructor.

I don't see what you're getting at here.

-- 
Andrea Faulds
http://ajf.me/

---

## #89254 — Anthony Ferrara — 2015-11-17T01:54:07+00:00

URL: https://externals.io/message/89223#89254 · In reply to: #89223

Chris,


> Hi,
>
> There has been a lot of interest recently (eg psr-7) in immutable data. I'm
> considering putting an RFC together to add language support for immutables:
>
> immutable class Foo {
> public $bar;
> public function __construct($bar) {
> $this->bar = $bar;
> }
> }
>
> Immutable on a class declaration makes all (maybe only public?) properties
> of the class immutable after construct; assigning to a property would
> result in a Fatal error.
>
> class Foo {
> public $bar;
> immutable public $baz;
> }
>
> Immutable on a property makes the property immutable once it takes on a
> none null value. Attempts to modify the property after this results in a
> fatal error.

Beyond syntax/whatever, I would suggest finding a method to do it
internally. That's going to be the biggest problem in my experience
(finding some way of doing it efficiently)...

Anthony

---

## #89255 — Andrea Faulds — 2015-11-17T02:06:39+00:00

URL: https://externals.io/message/89223#89255 · In reply to: #89223

Hi,

Chris Riley wrote:
>
> There has been a lot of interest recently (eg psr-7) in immutable data. I'm
> considering putting an RFC together to add language support for immutables:
>

I wonder if immutable classes are really the right way to go. Immutable 
reference types with manual copying are somewhat alien to PHP: instead, 
the language has used copy-on-write mutable value types for arrays and 
strings, and prior to PHP 5, objects.

Copy-on-write value types have all the benefits immutable types do. They 
can't be mutated at a distance unless you make a reference to them, 
trees made of them can be compared for equality with just ===, etc.

But they also have benefits that immutable types lack. They're easier to 
work with, because you can directly mutate them like everyone's used to 
doing: no messing around with withFooBar() and withoutFooBar(), you just 
set foobar or remove it directly. And PHP handles the duplication of 
objects for you implicitly, so there's far less code to write.

Unfortunately, PHP 5 got rid of PHP 4's value type objects and replaced 
them with reference type objects. But we could always introduce a way to 
get value type objects again. How about a `struct` keyword? It would be 
equivalent to `class`, but produce a value type rather than a reference 
type.

Any thoughts?

Thanks.
-- 
Andrea Faulds
http://ajf.me/

---

## #89256 — Chris Riley — 2015-11-17T07:18:40+00:00

URL: https://externals.io/message/89223#89256 · In reply to: #89255

Hi,

Chris Riley wrote:
>
> There has been a lot of interest recently (eg psr-7) in immutable data.
I'm
> considering putting an RFC together to add language support for
immutables:
>

I wonder if immutable classes are really the right way to go. Immutable
reference types with manual copying are somewhat alien to PHP: instead,
the language has used copy-on-write mutable value types for arrays and
strings, and prior to PHP 5, objects.

Copy-on-write value types have all the benefits immutable types do. They
can't be mutated at a distance unless you make a reference to them,
trees made of them can be compared for equality with just ===, etc.

But they also have benefits that immutable types lack. They're easier to
work with, because you can directly mutate them like everyone's used to
doing: no messing around with withFooBar() and withoutFooBar(), you just
set foobar or remove it directly. And PHP handles the duplication of
objects for you implicitly, so there's far less code to write.

Unfortunately, PHP 5 got rid of PHP 4's value type objects and replaced
them with reference type objects. But we could always introduce a way to
get value type objects again. How about a `struct` keyword? It would be
equivalent to `class`, but produce a value type rather than a reference
type.

Any thoughts?

Thanks.
--
Andrea Faulds
http:// <http://ajf.me/>ajf.me/ <http://ajf.me/>

--

To unsubscribe, visit: http:// <http://www.php.net/unsub.php>www.php.net
<http://www.php.net/unsub.php>/ <http://www.php.net/unsub.php>unsub.php
<http://www.php.net/unsub.php>


My main motivation for this was for event objects when doing event sourcing
- they are by definition unchangeable after creation. That said,
considering the wider use case there does seem to be a need to emulate the
with* functions.

One option would be to hide the messy constructor call within user defined
methods, but that would add a lot of boilerplate - something I was wanting
to reduce not increase.

I can't think of a clean easy way to add with* functionality at a language
level without even more magic (parameter skipping?)

As for setter injection - my initial proposal would support that - a
property declared immutable allows itself to be set - once.

If someone can give my wiki account rfc karma I can write this up so far
too help focus discussion.

~C

---

## #89259 — Alexander Lisachenko — 2015-11-17T08:12:38+00:00

URL: https://externals.io/message/89223#89259 · In reply to: #89256

Hello, internals!

We discussed this topic early, see this thread:
http://www.serverphorums.com/read.php?7,1123371,1124223 for any additional
thoughts and ideas. I want to attach a link to the Java draft of partial
immutability of objects:
http://cr.openjdk.java.net/~jrose/values/values-0.html, it's pretty
interesting and can be applied for PHP too.

I very like the whole idea of having native immutability for objects and
see a many cases where it can be applied.

I'm thinking about immutability for objects as passing variables as a copy
by value, not by reference. Const keyword can be used for that:

class Foo {
     public static function bar(const Baz $object) {  // Require an
instance of Baz to be passed as a copy, not a reference
         $object->test = '12345'; // Will throw an exception, because for
immutable object property fetching for writing is not allowed
         $object = 12345; // Will throw an exception, immutable variable
can not modified

         $myObject = $object; // Create our local copy of immutable object,
this will be a full copy (clone)
         $myObject->test = '456'; // OK
         echo $myObject->test, ' original is ', $object->test; // Will
output: '456 original is foo'
     }
}

$obj = new Baz; // Our local instance, #1
$obj->test = 'foo';
Foo::bar($obj); // Method requires immutability, so a new copy of $obj will
be created before passing value to the method

echo $obj->test; // Outputs 'foo'

Here, an original object will be preserved in any case, because a copy will
be used (probably, via cloning) and engine will check some restrictions to
keep variable immutability.

Some more code examples:

function foo() {
    const $bar = new stdClass; // We use const keyword with variable name
to declare it immutable
    $bar = 1; // Will throw an exception, because $bar is immutable const
variable
    $bar->test = 123; // Will throw an exception, because for immutable
variable which is object property fetch for writing is not allowed

    const $immutableArray = [1, 2, 3];
    $immutableArray[] = 4; // Immutable array can not be modified, only
read operation is supported

    $mutableArray = $immutableArray; // If we make a copy of variable
without const, it can be changed later via Copy-on-Write
    $mutableArray[] = 5; // OK, local copy is used
}

Function or methods can declare a return result to be immutable by adding a
"const" keyword into the declaration:

const function foo() : Baz // declare our function to return an immutable
copy of object
{
    $object = new Baz;
    $object->test = 456;
    return $object;
}

$value = foo(); // Will throw an exception, immutable return result from
the function can be assigned only to the immutable variable
const $immutableValue = foo(); // OK, immutable result is stored in the
immutable variable

ReflectionParameter, ReflectionFunctionAbstract can have an additional
methods: ReflectionParameter->isImmutable() or
RelectionParameter->isConst();
ReflectionFunctionAbstract->returnsImmutableValue()



2015-11-17 10:18 GMT+03:00 Chris Riley <t.carnage@gmail.com>:

> 
>
> Hi,
>
> Chris Riley wrote:
> > […]
> I'm
> > […]
> immutables:
> > […]
>
> I wonder if immutable classes are really the right way to go. Immutable
> reference types with manual copying are somewhat alien to PHP: instead,
> the language has used copy-on-write mutable value types for arrays and
> strings, and prior to PHP 5, objects.
>
> Copy-on-write value types have all the benefits immutable types do. They
> can't be mutated at a distance unless you make a reference to them,
> trees made of them can be compared for equality with just ===, etc.
>
> But they also have benefits that immutable types lack. They're easier to
> work with, because you can directly mutate them like everyone's used to
> doing: no messing around with withFooBar() and withoutFooBar(), you just
> set foobar or remove it directly. And PHP handles the duplication of
> objects for you implicitly, so there's far less code to write.
>
> Unfortunately, PHP 5 got rid of PHP 4's value type objects and replaced
> them with reference type objects. But we could always introduce a way to
> get value type objects again. How about a `struct` keyword? It would be
> equivalent to `class`, but produce a value type rather than a reference
> type.
>
> Any thoughts?
>
> Thanks.
> --
> Andrea Faulds
> http:// <http://ajf.me/>ajf.me/ <http://ajf.me/>
>
> --
> 
> To unsubscribe, visit: http:// <http://www.php.net/unsub.php>www.php.net
> <http://www.php.net/unsub.php>/ <http://www.php.net/unsub.php>unsub.php
> <http://www.php.net/unsub.php>
>
>
> My main motivation for this was for event objects when doing event sourcing
> - they are by definition unchangeable after creation. That said,
> considering the wider use case there does seem to be a need to emulate the
> with* functions.
>
> One option would be to hide the messy constructor call within user defined
> methods, but that would add a lot of boilerplate - something I was wanting
> to reduce not increase.
>
> I can't think of a clean easy way to add with* functionality at a language
> level without even more magic (parameter skipping?)
>
> As for setter injection - my initial proposal would support that - a
> property declared immutable allows itself to be set - once.
>
> If someone can give my wiki account rfc karma I can write this up so far
> too help focus discussion.
>
> ~C

---

## #89262 — Rowan Collins — 2015-11-17T11:04:04+00:00

URL: https://externals.io/message/89223#89262 · In reply to: #89253

Hi Andrea,

Andrea Faulds wrote on 17/11/2015 01:47:
> Larry Garfield wrote:
> > […]
>
> Huh?
>
> "with" methods and __construct are not exclusive. In fact, you could 
> have a private constructor and only have "with" methods. Only allowing 
> assignment to properties in the constructor doesn't prevent having a 
> nice API: have your methods call the constructor.
>
> I don't see what you're getting at here.
>

Making the "with" methods wrap the constructor tidies the public API, 
but that (private) constructor needs to be just as complex as Larry said 
earlier - you've got to manually extract all the properties of one 
instance, and pass them as parameters to the other. Or, you could copy 
them one by one inside the body of the constructor, which comes to the 
same thing - lots of boilerplate.

If you look at the PSR-7 implementation I linked to earlier [1], there's 
no such boilerplate, just a single call to "clone $this", and PHP does 
it all for you. But that doesn't work with a simplistic definition of 
immutable like "mutable in constructor" or "mutable until non-null"; you 
need to be able to "freeze" an instance once you've set it up, or have 
privileged (private) methods which are allowed to mutate the properties.

[1] https://github.com/guzzle/psr7/blob/master/src/Request.php#L101

Regards,
-- 
Rowan Collins
[IMSoP]

---

## #89263 — Chris Riley — 2015-11-17T11:19:47+00:00

URL: https://externals.io/message/89223#89263 · In reply to: #89262

> Hi Andrea,
>
> Andrea Faulds wrote on 17/11/2015 01:47:
>
> > […]
> Making the "with" methods wrap the constructor tidies the public API, but
> that (private) constructor needs to be just as complex as Larry said
> earlier - you've got to manually extract all the properties of one
> instance, and pass them as parameters to the other. Or, you could copy them
> one by one inside the body of the constructor, which comes to the same
> thing - lots of boilerplate.
>
> If you look at the PSR-7 implementation I linked to earlier [1], there's
> no such boilerplate, just a single call to "clone $this", and PHP does it
> all for you. But that doesn't work with a simplistic definition of
> immutable like "mutable in constructor" or "mutable until non-null"; you
> need to be able to "freeze" an instance once you've set it up, or have
> privileged (private) methods which are allowed to mutate the properties.
>
> [1] https://github.com/guzzle/psr7/blob/master/src/Request.php#L101
>
> Regards,
> --
> Rowan Collins
> [IMSoP]
>
> --
> 
> 
>
>
Could clone take an array parameter of immutable property overrides?

$copied = clone ($immutable, ['foo' => 'bar']);

---

## #89266 — Lester Caine — 2015-11-17T12:26:51+00:00

URL: https://externals.io/message/89223#89266 · In reply to: #89262

> If you look at the PSR-7 implementation I linked to earlier [1], there's
> no such boilerplate, just a single call to "clone $this", and PHP does
> it all for you. But that doesn't work with a simplistic definition of
> immutable like "mutable in constructor" or "mutable until non-null"; you
> need to be able to "freeze" an instance once you've set it up, or have
> privileged (private) methods which are allowed to mutate the properties.

read_only=true; ... after the value is set.

Switch off all the 'setter' magic once the object has been created, but
I still see a problem where this only really works with simple values?
If I've created a more complex object, while it's 'value' may need to be
fixed, how it's displayed may involve setting alternative defaults, so
one still needs to be able to set other variables in the object.

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #89267 — Mathieu Rochette — 2015-11-17T13:40:54+00:00

URL: https://externals.io/message/89223#89267 · In reply to: #89266

> 
> > […]
Indeed, maybe there is a use for a new language construct to help reduce 
the boilerplate, but it could be done afterward, the "everything in the 
constructor" works fine. If there is too much in the construct it might 
be a sign that the class is doing too much and should be refactored in 
smaller classes

slightly off topic, in rust you initialize struct like that :

MyStruct {
   myprop1: myvalue1,
   myprop2: myvalue2,
}

It could be extended in php to have something like that :

class MyClass {
   private $myProp1;
   private $myProp2;

   static create function ($prop1) {
     return new self {
       myProp1: $prop1;
       myProp2: 'initialValue'
     };
   }

   public function withProp2($prop2) {
     return new self {
       myProp2: $prop2,
       *: $this, // here * would mean: properties other than myProp2 are 
taken from $this
     };
     // or like that
     return self {
       myProp2: $prop2,
       ... $this, // properties other than myProp2 are taken from $this
     };
   }
}

note: I'd like this kind of construct even without the immutable stuff 
(would avoid having to write boilerplate __construct)
> read_only=true; ... after the value is set.
>
> Switch off all the 'setter' magic once the object has been created, but
> I still see a problem where this only really works with simple values?
> If I've created a more complex object, while it's 'value' may need to be
> fixed, how it's displayed may involve setting alternative defaults, so
> one still needs to be able to set other variables in the object.
In that case if you don't want to make its "display settings" immutable 
you should have 2 classes, one with the immutable 'value' and another 
containing the 'value' and the "display settings"

---

## #89268 — Andrea Faulds — 2015-11-17T15:37:21+00:00

URL: https://externals.io/message/89223#89268 · In reply to: #89262

Hi,

Rowan Collins wrote:
> Hi Andrea,
>
> Andrea Faulds wrote on 17/11/2015 01:47:
> > […]
>
> Making the "with" methods wrap the constructor tidies the public API,
> but that (private) constructor needs to be just as complex as Larry said
> earlier - you've got to manually extract all the properties of one
> instance, and pass them as parameters to the other. Or, you could copy
> them one by one inside the body of the constructor, which comes to the
> same thing - lots of boilerplate.

Does it? You can write a five-line constructor which does what you need:

     private function __construct(array $properties) {
         foreach ($properties as $name => $value) {
             $this->{$name} = $value;
         }
     }

You can do changes like so:

     public function withName(string $name): self {
         return new self(array_merge((array)$this, [
             "name" => $name
         ]));
     }

Not much code, no?

Thanks.

-- 
Andrea Faulds
http://ajf.me/

---

## #89269 — Rowan Collins — 2015-11-17T15:41:53+00:00

URL: https://externals.io/message/89223#89269 · In reply to: #89267

Mathieu Rochette wrote on 17/11/2015 13:40:
> Indeed, maybe there is a use for a new language construct to help 
> reduce the boilerplate, but it could be done afterward, the 
> "everything in the constructor" works fine. If there is too much in 
> the construct it might be a sign that the class is doing too much and 
> should be refactored in smaller classes 

Even with a small number of members, manually reconstructing the object 
is a nuisance  - imagine if you have 4 properties, and 10 "with" 
methods; not particularly complex for one class. But with "mutable only 
in the constructor", adding a 5th property would require changing all 10 
methods, for absolutely zero gain.

It's certainly doable, and if the clone keyword didn't already exist, it 
would be a reasonable thing to do; but the gains of using "clone" (in 
terms of code readability and robustness) rather outweigh the gains of 
marking the class immutable, if the implementation is such that you're 
forced to choose.

Regards,
-- 
Rowan Collins
[IMSoP]

---

## #89270 — Rowan Collins — 2015-11-17T16:26:02+00:00

URL: https://externals.io/message/89223#89270 · In reply to: #89268

Andrea Faulds wrote on 17/11/2015 15:37:
> Does it? You can write a five-line constructor which does what you need:
>
>     private function __construct(array $properties) {
>         foreach ($properties as $name => $value) {
>             $this->{$name} = $value;
>         }
>     }
>
> You can do changes like so:
>
>     public function withName(string $name): self {
>         return new self(array_merge((array)$this, [
>             "name" => $name
>         ]));
>     }
>
> Not much code, no? 

So, you need to reinvent "clone", in a less efficient, less readable, 
way; restrict the form of your constructor, and the style of 
implementation in your withName method; and for what gain exactly? 
Implementations of this pattern using clone already exist, they are 
efficient, clear, and maintainable; I'm not sure the promise of run-time 
checking for mistakes would entice anyone to change them.

Would the modification be restricted to the lexical scope of the 
constructor, or the dynamic scope? i.e. would this be allowed?

private function __construct($foo) {
     $this->setFoo($foo);
}

If that's allowed, then you basically have a "freeze" action internally 
anyway, that's called when the constructor returns; it's not a great 
leap from there to having it exposed to userland to choose when it 
happens. If that's not allowed, and the modifications have to be in the 
constructor body itself, the value of the feature is further reduced.

That said, I'd be interested in examples of what any other languages do 
around this area, and how it is used in practice.

Regards,
-- 
Rowan Collins
[IMSoP]

---

Next page: https://externals.io/message/89223.md?page=2
