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
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
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
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
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
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
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.Any differance from the final keyword?
http://php.net/manual/en/language.oop5.final.php
On Mon, Nov 16, 2015 at 10:15 AM, Chris Riley t.carnage@gmail.com
wrote: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
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
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: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
On Mon, Nov 16, 2015 at 10:15 AM, Chris Riley t.carnage@gmail.com
wrote: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?
~CWhat 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?
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 immutableI 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
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
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 ?
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
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]
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
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?
~CAs 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.
agreedThe 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_propertiesThe 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
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?
~CAs 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.
agreedThe 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 ?
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. :-)
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 ?
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.
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
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
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/
Hi Andrea,
Andrea Faulds wrote on 17/11/2015 01:47:
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.
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]
Hi Andrea,
Andrea Faulds wrote on 17/11/2015 01:47:
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.
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']);
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
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.
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"
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]
Hi,
Rowan Collins wrote:
Hi Andrea,
Andrea Faulds wrote on 17/11/2015 01:47:
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.
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/
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]
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.
Any thoughts?
Would love to have it for implementing value objects.
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
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/
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.phpwww.php.net
http://www.php.net/unsub.php/ http://www.php.net/unsub.phpunsub.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
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:
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 astruct
keyword? It would be
equivalent toclass
, 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.phpwww.php.net
http://www.php.net/unsub.php/ http://www.php.net/unsub.phpunsub.php
http://www.php.net/unsub.phpMy 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
FYI there was a very small discussion about it ~ 1 year ago
internals@lists.php.net/msg71521.html" rel="nofollow" target="_blank">https://www.mail-archive.com/internals@lists.php.net/msg71521.html
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
FYI there was a very small discussion about it ~ 1 year ago
internals@lists.php.net/msg71521.html" rel="nofollow" target="_blank">https://www.mail-archive.com/internals@lists.php.net/msg71521.html
And https://www.mail-archive.com/internals%40lists.php.net/msg61320.html
covered making properties read_only ...
--
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