# [RFC] [Discussion] Clone with

- Thread: https://externals.io/message/120048
- Original archive: https://news-web.php.net/php.internals/120048
- Started by kocsismate90@gmail.com on 2023-04-17T06:32:19+00:00
- Messages: 55
- RFC: Clone with v2 (Implemented (PHP 8.5)) — https://externals.io/rfc/clone_with_v2.md
- RFC: Write-Once Properties (Declined) — https://externals.io/rfc/write_once_properties.md
- RFC: Clone with (Inactive) — https://externals.io/rfc/clone_with.md
- Page 1 of 3: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/120048.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #120048 — kocsismate90@gmail.com — 2023-04-17T06:32:19+00:00

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

Hi Everyone,

Quite some time after mentioning the "clone with" construct the first time
(at the end of the
https://wiki.php.net/rfc/write_once_properties#run-time_behaviour section),
finally I managed to create a working implementation for this feature which
would make it possible to properly modify readonly properties
while simplifying how we write "wither" methods:
https://wiki.php.net/rfc/clone_with

Regards,
Máté Kocsis

---

## #120049 — drealecs@gmail.com — 2023-04-17T07:43:50+00:00

URL: https://externals.io/message/120048#120049 · In reply to: #120048

> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with


Hey Máté,

How about just allowing a block of code after the clone statement that
would execute it in the same context as the clone context, allowing to
modify once readonly variables.
Allows better flexibility compared with clone with syntax or clone metho:

    public function withStatus($code, $reasonPhrase = ''): Response
    {
        return clone $this {
            $this->statusCode = $code;

           $this->reasonPhrase = $reasonPhrase;

};

Regards,
Alex

---

## #120050 — michal.brzuchalski@gmail.com — 2023-04-17T08:05:32+00:00

URL: https://externals.io/message/120048#120050 · In reply to: #120048

Hi Máté,

pon., 17 kwi 2023 o 08:32 Máté Kocsis <kocsismate90@gmail.com> napisał(a):

> Hi Everyone,
>
> Quite some time after mentioning the "clone with" construct the first time
> (at the end of the
> https://wiki.php.net/rfc/write_once_properties#run-time_behaviour
> section),
> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with


Thanks for your efforts and for bringing that up.
I am curious if possible to implement the feature without using `with`
keyword
it visually could look pretty close to something like an object initializer
in the future:

return clone $this {c: 1};
return new Bar {c: 1};

Cheers,
Michał Marcin Brzuchalski

---

## #120057 — fekzol.13ker@gmail.com — 2023-04-17T20:13:19+00:00

URL: https://externals.io/message/120048#120057 · In reply to: #120050

Hey,

> public function withStatus($code, $reasonPhrase = ''): Response
> {
>     return clone $this {
>         $this->statusCode = $code;
>
>         $this->reasonPhrase = $reasonPhrase;
>     }
> };

How to refer to any of the properties of the current instance? Let's say
this:

```
class Foo {
    protected int $code;
    protected string $message;
    public function withStatus($code, $message): Foo {
        return clone $this {
            $this->code = $code; // so far so good
            // Which $this->message is what?
            $this->message = "cloned: (" . $this->message . ")" . $message;
        }
    }
}
```

> Thanks for your efforts and for bringing that up.
> I am curious if possible to implement the feature without using `with`
> keyword
> it visually could look pretty close to something like an object
initializer
> in the future:
>
> return clone $this {c: 1};
> return new Bar {c: 1};

Similarity is not necessarily always good.

Regards,
Zoltán Fekete

---

## #120058 — drealecs@gmail.com — 2023-04-17T21:00:39+00:00

URL: https://externals.io/message/120048#120058 · In reply to: #120057

Hi Zoltán,
On Mon, Apr 17, 2023 at 11:13 PM Zoltán Fekete <fekzol.13ker@gmail.com>
wrote:

> Hey,


> > […]
>
> How to refer to any of the properties of the current instance? Let's say
> this:
>
> ```
> class Foo {
>     protected int $code;
>     protected string $message;
>     public function withStatus($code, $message): Foo {
>         return clone $this {
>             $this->code = $code; // so far so good
>             // Which $this->message is what?
>             $this->message = "cloned: (" . $this->message . ")" . $message;
>         }
>     }
> }
> ```


Yes, it's true that it looks weird that $this is present in two places with
two different meanings, but that's just because we are cloning $this. If we
were cloning $object, it would be clearer.
$this inside the braces is referring to the new object after it was cloned,
after __clone() was executed, if defined.
The old instance data is already copied over, no real need to reference
that.

Maybe we can think about it a bit more and improve the idea. I'm
thinking we can have a closure/callable or an instance method reference
instead of a block of code.
Also, that might be preferable, as there is a natural way to either use
closure with "use" or pass parameters.
I don't like too much how $code and $message are referenced inside the
block of code... maybe it's not an issue.
Also, I'm not really sure how easy it would be to model this behavior, to
have a block of code where $this would reference something else than
outside of it.

Regards,
Alex

---

## #120062 — Rowan Tommins — 2023-04-18T08:10:00+00:00

URL: https://externals.io/message/120048#120062 · In reply to: #120048

> https://wiki.php.net/rfc/clone_with


I look the look of this; very concise.

A couple of clarifications that might be useful to add to the RFC:

1) You mention in the Alternatives sometimes needing access to the 
original instance; it would be good to have an example of how this looks 
with the clone-with syntax.

2) How does this interact with an __clone() method? I'm guessing the 
__clone() would be called first, and then the with-clause applied?


Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #120063 — tim@bastelstu.be — 2023-04-18T08:19:54+00:00

URL: https://externals.io/message/120048#120063 · In reply to: #120062

Hi


> 2) How does this interact with an __clone() method? I'm guessing the
> __clone() would be called first, and then the with-clause applied?
> 

More generally the order of operations with regard to possible side 
effects and/or exceptions would be interesting.

    clone $something with {
      foo() => bar(),
      quux() => baz(),
    };

In which order will __clone(), foo(), bar(), quux(), baz() be evaluated 
and what will happen if one of them throws an Exception? Will 
destructors of the cloned object run? When?

Best regards
Tim Düsterhus

---

## #120064 — michal.brzuchalski@gmail.com — 2023-04-18T09:16:37+00:00

URL: https://externals.io/message/120048#120064 · In reply to: #120063

wt., 18 kwi 2023 o 10:20 Tim Düsterhus <tim@bastelstu.be> napisał(a):

> Hi
>
> 
> > […]
>
> More generally the order of operations with regard to possible side
> effects and/or exceptions would be interesting.
>
>     clone $something with {
>       foo() => bar(),
>       quux() => baz(),
>     };
>

Just noticed the "Property name expressions" and am wondering if it could
be a separate feature
allowing for passing named arguments to functions/constructors in the same
fashion?

$something = new Something(
      foo() => bar(),
      quux() => baz(),
);

Cheers,
Michał Marcin Brzuchalski

---

## #120065 — Andreas Hennings — 2023-04-18T09:27:20+00:00

URL: https://externals.io/message/120048#120065 · In reply to: #120048

Hello Máté, internals,
I have been waiting for this to happen :)
Some feedback

> However, in some cases it would be useful to reference property names as expressions, e.g. when one needs to use “clone with” in a foreach loop where the index is the property name and the loop variable is the value to be assigned. This is also possible using a slightly more verbose syntax:

What about argument unpacking?
I don't know if we can combine this with ":" syntax or only with "=>".

clone $object with {
  ...$arr,
  ...['a' => 'A', $b => 'B'],
  ...(function () {yield 'x' => 'X';})(),
  c: 'C',  // or
  d => 'D',  // No mixing of ':' and '=>', we have to choose one.
}

If we want to go crazy in the future:
(This would require another language feature of inline code blocks
with return value or behaving as generator)

clone $object with {
  ...{
    yield 'a' => 'A';
    yield 'b' => 'B';
  },
  ...{
    return ['c' => 'C'],
  }
}

-- Andreas


>
> Hi Everyone,
>
> Quite some time after mentioning the "clone with" construct the first time
> (at the end of the
> https://wiki.php.net/rfc/write_once_properties#run-time_behaviour section),
> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with
>
> Regards,
> Máté Kocsis

---

## #120066 — Andreas Hennings — 2023-04-18T09:40:04+00:00

URL: https://externals.io/message/120048#120066 · In reply to: #120048

> The biggest advantage of Nicolas' proposal over “clone with” is that it could be made part of the interface contract whether a method modifies the object state. So the PSR-7 ResponseInterface could look like the following:
> [..]
> public clone function withStatus($code, $reasonPhrase = '');

If this is the main argument, I think I would prefer a more general
"readonly" modifier on methods.

public readonly function withStatus($code, $reasonPhrase = ''): static;

> One cannot control whether $this should really be cloned: e.g. if a property should only be modified based on certain conditions (e.g. validation), the object would potentially be cloned in vain, resulting in a performance loss.

Exactly. I would say "conditionally clone", e.g. only clone if there
is a change, or if we don't already have a cached instance with this
value.

I think the "clone with" is much more flexible because we can call
this anywhere, not just in a dedicated method.

This said, I do agree with some of the benefits of the "Alternative" proposal.

-- Andreas



>
> Hi Everyone,
>
> Quite some time after mentioning the "clone with" construct the first time
> (at the end of the
> https://wiki.php.net/rfc/write_once_properties#run-time_behaviour section),
> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with
>
> Regards,
> Máté Kocsis

---

## #120067 — Larry Garfield — 2023-04-18T15:45:46+00:00

URL: https://externals.io/message/120048#120067 · In reply to: #120049

> 
>
> > […]

A solid RFC, thanks!

I agree with the discussion of Nicolas's alternative, which has its merits but also some possible limitations.  My only major concern myself is the potential confusion between when to use : and when to use =>.  Using the => version only for dynamic keys feels very clunky.  In fact, I think the RFC is inconsistent on this front:

$object = clone $object with {"foo" => 1}; // the property name is a literal
$object = clone $object with {strtolower("FO") . "o" => 1}; // the property name is an expression
$object = clone $object with {PROPERTY_NAME => 1}; // the property name is a named constant

I would expect the first one to be a colon, not fat-arrow.  Is that a typo?

Is there a technical reason why they can't just all use a colon?  That would extend more nicely to supporting dynamic keys in the future for named arguments.

> Hey Máté,
>
> How about just allowing a block of code after the clone statement that
> would execute it in the same context as the clone context, allowing to
> modify once readonly variables.
> Allows better flexibility compared with clone with syntax or clone metho:
>
>     public function withStatus($code, $reasonPhrase = ''): Response
>     {
>         return clone $this {
>             $this->statusCode = $code;
>
>            $this->reasonPhrase = $reasonPhrase;
>
> };

This is an interesting alternative.  I don't think $this is that confusing, actually.  Inside __clone(), $this always refers to the new object.  This proposal would essentially be providing an additional __clone block that runs in the same context, viz, $this always refers to the new object.  I'm not sure if you need the original object separately at all, in fact; if you want to look up a value from the original object... you already have it on the new object in the same location, because it's a clone.  So:

public function withStatus($code, $reasonPhrase = ''): Response
{
    return clone $this {
        $this->statusCode = $code;
         $this->reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
    }
};

$this->reasonPhrase in the string refers to the cloned object, where that value is by definition identical to the original object.  It's evaluated, the string is computed, and then the clone's reasonPhrase property is updated.

The catch here is 2 fold:

1. This would essentially imply auto-capture.  While I am quite OK with that, a fair number of people are not.  But adding a use() block to that syntax just makes it very clunky for the obvious common case; which is basically the above, so it just forces you to repeat each variable name a third time.  I would probably vote against something that forced more redundant use() statements.

2. The scope permissioning becomes weirder.  The current RFC syntax is very predictable: properties get assigned from the context at which the clone statement lives, and if there are private/protected restrictions those are self-evident.  With the more closure-like approach, it "feels" like you're providing an anon function that will get bound to the new object and then executed... which would then always run with private scope and be able to modify values you probably didn't want to be publicly modifiable.  While we could potentially implement it such that the scope rules are still enforced, that may not be as obvious why you can/can't mess with a particular variable.

I'm open to this alternative though if Mate is; it has potential, but we'd have to sort out the above.

--Larry Garfield

---

## #120068 — tim@bastelstu.be — 2023-04-18T16:48:00+00:00

URL: https://externals.io/message/120048#120068 · In reply to: #120067

Hi


> I agree with the discussion of Nicolas's alternative, which has its merits but also some possible limitations.  My only major concern myself is the potential confusion between when to use : and when to use =>.  Using the => version only for dynamic keys feels very clunky.  In fact, I think the RFC is inconsistent on this front:
> 
> $object = clone $object with {"foo" => 1}; // the property name is a literal
> $object = clone $object with {strtolower("FO") . "o" => 1}; // the property name is an expression
> $object = clone $object with {PROPERTY_NAME => 1}; // the property name is a named constant
> 
> I would expect the first one to be a colon, not fat-arrow.  Is that a typo?

No, that is correct. Colon takes a bare identifier, fat-arrow takes an 
expression (that just happens to be a constant expression in that case).

> Is there a technical reason why they can't just all use a colon?  That would extend more nicely to supporting dynamic keys in the future for named arguments.

I'd rather see only the fat-arrow being allowed. Unless I'm missing 
something, braces with colon is not used anywhere else, whereas braces + 
'=>' is known from match() and '=>' more generally is already used with 
array literals [1]. Having two similar syntaxes for the same thing is 
not great when both are commonly needed is not great. They need to be 
documented and learned by developers.

Best regards
Tim Düsterhus

[1] In fact if the right hand side of with may be an expression that 
evaluates to an array, folks wouldn't need to learn new syntax at all:

     $newProperties = [ "foo" => "bar" ];
     clone $object with $newProperties;

and

     clone $object with [ "foo" => "bar" ];

---

## #120069 — Bob Magic — 2023-04-18T20:00:29+00:00

URL: https://externals.io/message/120048#120069 · In reply to: #120068

> [1] In fact if the right hand side of with may be an expression that
> evaluates to an array, folks wouldn't need to learn new syntax at all:
>
>     $newProperties = [ "foo" => "bar" ];
>     clone $object with $newProperties;
>
> and
>
>     clone $object with [ "foo" => "bar" ];

in my opinion this is sick, as in awesome. the {} version makes no sense to
me unless the language gains $var = {} ; to skip $var = (object)[];

---

## #120072 — Andreas Hennings — 2023-04-19T15:26:58+00:00

URL: https://externals.io/message/120048#120072 · In reply to: #120069

>
> > […]
>
> in my opinion this is sick, as in awesome. the {} version makes no sense to
> me unless the language gains $var = {} ; to skip $var = (object)[];

I think the {...} is meant to be simply an "argument list", not a value.
But yes having an array here would add some flexibility. It would also
give us argument unpacking for free.

I wonder what this means for performance.
Will the expression always be evaluated as an array first, and then
applied, or can php do a "shortcut" where it internally treats it as
an argument list, even though the syntax implies array?
The different will be small, but it could become relevant if called
many times over.

-- Andreas

---

## #120073 — tim@bastelstu.be — 2023-04-19T15:45:55+00:00

URL: https://externals.io/message/120048#120073 · In reply to: #120072

Hi


> I wonder what this means for performance.
> Will the expression always be evaluated as an array first, and then
> applied, or can php do a "shortcut" where it internally treats it as
> an argument list, even though the syntax implies array?
> The different will be small, but it could become relevant if called
> many times over.

I've said it before, but I'm going to say it again: Performance should 
be low in the list of things that decide how a feature will look. 
Opcache and the optimizer can always be improved in the future, syntax 
cannot.

Of course if a feature is unacceptably slow, it is a non-starter. But in 
this case I expect it to just be "there is optimization potential", as 
creating an array with a handful of elements shouldn't be unacceptably 
slow and also 'clone with' is likely going to make up only a very small 
part of a request's entire processing.

Best regards
Tim Düsterhus

---

## #120075 — Rowan Tommins — 2023-04-20T07:00:49+00:00

URL: https://externals.io/message/120048#120075 · In reply to: #120068

>I'd rather see only the fat-arrow being allowed. Unless I'm missing something, braces with colon is not used anywhere else, whereas braces + '=>' is known from match() and '=>' more generally is already used with array literals [1]. Having two similar syntaxes for the same thing is not great when both are commonly needed is not great. They need to be documented and learned by developers.


I think it makes sense to have an unquoted form here, because the common case is that they are names which analysers can match statically to particular properties, not strings which will be analysed at runtime. There are plenty of places in the language where dynamic names are allowed, but we don't just use strings for the static case:

${'foo'} = 'bar'( constant('BAZ') )->{'quux'}();


More specifically, the "name: $value" syntax matches named parameters, and while you can use an array for that (via ... unpacking), we don't force users to do so.

In fact, the Future Scope of that RFC considered the opposite: using the colon syntax in arrays, along with a shorthand for pulling the key name from the variable name: https://wiki.php.net/rfc/named_params#shorthand_syntax_for_matching_parameter_and_variable_name

>  If I wanted to put these ideas into a general framework, I think one way to go about this would be as follows:
>
> - Consider identifier: $expr as a shorthand for "identifier" => $expr.
 > - Consider :$variable as a shorthand for variable: $variable and thus "variable" => $variable.


That would give us:

$point = ['x' => $x, 'y' => $y, 'z' => $z];
$point = [x: $x, y: $y, z: $z];
$point = [:$x, :$y, :$z];

$point = new Point(...['x' => $x, 'y' => $y, 'z' => $z]);
$point = new Point(x: $x, y: $y, z: $z);
$point = new Point(:$x, :$y, :$z);

$point = clone $point with {'x' => $x, 'y' => $y, 'z' => $z};
$point = clone $point with {x: $x, y: $y, z: $z};
$point = clone $point with {:$x, :$y, :$z};



Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking:

$point = clone $point with (x: $x, y: $y, z: $z);
$point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]);



Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #120076 — Robert Landers — 2023-04-20T07:11:27+00:00

URL: https://externals.io/message/120048#120076 · In reply to: #120075

>
> 
> > […]
>
>
> I think it makes sense to have an unquoted form here, because the common case is that they are names which analysers can match statically to particular properties, not strings which will be analysed at runtime. There are plenty of places in the language where dynamic names are allowed, but we don't just use strings for the static case:
>
> ${'foo'} = 'bar'( constant('BAZ') )->{'quux'}();
>
>
> More specifically, the "name: $value" syntax matches named parameters, and while you can use an array for that (via ... unpacking), we don't force users to do so.
>
> In fact, the Future Scope of that RFC considered the opposite: using the colon syntax in arrays, along with a shorthand for pulling the key name from the variable name: https://wiki.php.net/rfc/named_params#shorthand_syntax_for_matching_parameter_and_variable_name
>
> > […]
>
>
> That would give us:
>
> $point = ['x' => $x, 'y' => $y, 'z' => $z];
> $point = [x: $x, y: $y, z: $z];
> $point = [:$x, :$y, :$z];
>
> $point = new Point(...['x' => $x, 'y' => $y, 'z' => $z]);
> $point = new Point(x: $x, y: $y, z: $z);
> $point = new Point(:$x, :$y, :$z);
>
> $point = clone $point with {'x' => $x, 'y' => $y, 'z' => $z};
> $point = clone $point with {x: $x, y: $y, z: $z};
> $point = clone $point with {:$x, :$y, :$z};
>
>
>
> Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking:
>
> $point = clone $point with (x: $x, y: $y, z: $z);
> $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]);
>
>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php
>

I think using arrays here makes a lot of sense.

For example, this example:

public function withStatus($code, $reasonPhrase = ''): Response
{
    return clone $this {
        $this->statusCode = $code;
         $this->reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
    }
};

could be rewritten with

public function withStatus($statusCode, $reasonPhrase = ''): Response {
  // perform validation here
  $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
  return clone $this with compact('statusCode', 'reasonPhrase');
}

I, personally, would find this much more ergonomic than writing out
blocks of code and having a totally different syntax.

---

## #120077 — Aleksander Machniak — 2023-04-20T07:22:11+00:00

URL: https://externals.io/message/120048#120077 · In reply to: #120075

> Rather than making everything use an array or array-like syntax, I would probably go the other way and scrap the special syntax for dynamic names, making the whole thing look like a function call, with support for array unpacking:
> 
> $point = clone $point with (x: $x, y: $y, z: $z);
> $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]);

or $point = clone($point, x: $x, y: $y, z: $z);

Also, I didn't see it mentioned, but maybe for future scope, these new 
arguments should be passed to __clone().

-- 
Aleksander Machniak
Kolab Groupware Developer        [https://kolab.org]
Roundcube Webmail Developer  [https://roundcube.net]
----------------------------------------------------
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

---

## #120078 — Rowan Tommins — 2023-04-20T08:21:12+00:00

URL: https://externals.io/message/120048#120078 · In reply to: #120076

> public function withStatus($code, $reasonPhrase = ''): Response
> {
>      return clone $this {
>          $this->statusCode = $code;
>           $this->reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
>      }
> };


Note that this is not the current proposed syntax. Since the keys are 
not dynamic, the current proposal is this:

public function withStatus($statusCode, $reasonPhrase = ''): Response {
   // perform validation here
   $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
   return clone $this with {statusCode: $statusCode, reasonPhrase: $reasonPhrase};
}



> public function withStatus($statusCode, $reasonPhrase = ''): Response {
>    // perform validation here
>    $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
>    return clone $this with compact('statusCode', 'reasonPhrase');
> }


The `compact()` function always feels like a relic of the same era as 
create_function() and `call_user_func()`, both of which now have dedicated 
syntax.

That's what Nikita was talking about in the RFC section I quoted 
earlier: that compact('foo', 'bar') could be replaced with a dedicated 
syntax like [:$foo, :$bar]

So if we insisted on arrays, that would be:

public function withStatus($statusCode, $reasonPhrase = ''): Response {
   // perform validation here
   $reasonPhrase = "Old: $this->reasonPhrase, New: $reasonPhrase";
   return clone $this with [:$statusCode, :$reasonPhrase];
}

But I still don't see why an array should be the default case here, 
rather than using ... to unpack one if you really need to, like we do 
with arguments.

public function withStatus($statusCode, $reasonPhrase = null): Response {
   $newProps = [:$statusCode];
   if ( $reasonPhrase !== null ) {
       $newProps['reasonPhrase'] = "Old: $this->reasonPhrase, New: $reasonPhrase";
   }
   return clone $this with (...$newProps);
}


Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #120079 — Larry Garfield — 2023-04-20T14:15:29+00:00

URL: https://externals.io/message/120048#120079 · In reply to: #120077

> 
> > […]
>
> or $point = clone($point, x: $x, y: $y, z: $z);
>
> Also, I didn't see it mentioned, but maybe for future scope, these new 
> arguments should be passed to __clone().

They should not.  See https://peakd.com/hive-168588/@crell/object-properties-part-2-examples .  I went through and experimented with different syntaxes, and passing arguments to __clone() was by far the worst option in practice. :-)

--Larry Garfield

---

## #120080 — Larry Garfield — 2023-04-20T14:17:45+00:00

URL: https://externals.io/message/120048#120080 · In reply to: #120075

> Rather than making everything use an array or array-like syntax, I 
> would probably go the other way and scrap the special syntax for 
> dynamic names, making the whole thing look like a function call, with 
> support for array unpacking:
>
> $point = clone $point with (x: $x, y: $y, z: $z);
> $point = clone $point with (...['x' => $x, 'y' => $y, 'z' => $z]);

I agree here, for all the reasons Rowan indicated.  We already have a perfectly good syntax and semantics for named arguments that supports splat.  Using that here would handle all the use cases we care about, including dynamic names, without any additional syntax.  

Let's not shoe-horn arrays in here where they're not needed.  Making arrays themselves fancier/more compact with an alternate syntax is worth discussing, but that should be a separate RFC.

--Larry Garfield

---

## #120081 — tim@bastelstu.be — 2023-04-20T14:39:37+00:00

URL: https://externals.io/message/120048#120081 · In reply to: #120080

Hi


> 
> 
> > […]
> 
> I agree here, for all the reasons Rowan indicated.  We already have a perfectly good syntax and semantics for named arguments that supports splat.  Using that here would handle all the use cases we care about, including dynamic names, without any additional syntax.
> 

FWIW I'm not too attached to my array proposal [1]. I would also be fine 
with Rowan's proposal of making the "with()" syntactically identical to 
a function call, if that's more agreeable.

But please no entirely new syntax with braces as it currently is shown 
in the examples in the RFC.

Best regards
Tim Düsterhus

[1] Though I still consider arrays to be more "natural" than named 
arguments.

---

## #120082 — Aleksander Machniak — 2023-04-20T17:17:33+00:00

URL: https://externals.io/message/120048#120082 · In reply to: #120079

> > […]
> 
> They should not.  See https://peakd.com/hive-168588/@crell/object-properties-part-2-examples .  I went through and experimented with different syntaxes, and passing arguments to __clone() was by far the worst option in practice. :-)

Sure, I didn't propose it to be a solution for "clone with", but rather, 
if we implement "clone with" (no matter in which way) this is "cloning 
an object with extra properties", so therefore one would ask to have 
access to these extra properties inside __clone(). I'm not sure how 
useful that could be, but maybe worth mentioning in future scope or 
somewhere in the RFC.

And I think this syntax should still be on the table, no need for a new 
keyword.

$point = clone($point, x: $x, y: $y, z: $z);

-- 
Aleksander Machniak
Kolab Groupware Developer        [https://kolab.org]
Roundcube Webmail Developer  [https://roundcube.net]
----------------------------------------------------
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

---

## #120087 — michal.brzuchalski@gmail.com — 2023-04-20T19:58:25+00:00

URL: https://externals.io/message/120048#120087 · In reply to: #120081

Hi Tim,

czw., 20 kwi 2023 o 16:39 Tim Düsterhus <tim@bastelstu.be> napisał(a):

> ...
> But please no entirely new syntax with braces as it currently is shown
> in the examples in the RFC.
>

Then we should vote for syntax. Personally, I prefer braces here
because it doesn't look like a regular function call allowing easily to
distinguish
between two different things.

Cheers,
Michał Marcin Brzuchalski

---

## #120115 — kocsismate90@gmail.com — 2023-04-23T23:01:01+00:00

URL: https://externals.io/message/120048#120115 · In reply to: #120049

Hey Everyone,

Thank you for the lot of feedback! Sorry, I'm going to have to answer in a
single email otherwise I would have to send too many emails.

Alexandru wrote:

 How about just allowing a block of code after the clone statement that
> would execute it in the same context as the clone context, allowing to
> modify once readonly variables.
> Allows better flexibility compared with clone with syntax or clone method:


 This is an interesting alternative, but as Tim pointed out in a later
reply, the block would run in the private scope, which is contrary to my
intentions (and what I consider
the best solution).

Michał wrote:

I am curious if possible to implement the feature without using `with`
> keyword
> it visually could look pretty close to something like an object
> initializer in the future:
> return clone $this {c: 1};
> return new Bar {c: 1};


That's exactly what I tried first, but unfortunately, this syntax led to
parser ambiguities, so at last I had to settle on using "with".

Rowan wrote:

1) You mention in the Alternatives sometimes needing access to the
> original instance; it would be good to have an example of how this looks
> with the clone-with syntax.


Makes sense, so I've just come up with an example where a linked list of
objects are created. Let me know if you have a better example :)

2) How does this interact with an __clone() method? I'm guessing the
> __clone() would be called first, and then the with-clause applied?


Yeah, thanks for pointing this out! I agree that the clarification is very
much needed. BTW the evaluation order
is exactly how you wrote. This is now added to the RFC.

Tim wrote:

> In which order will __clone(), foo(), bar(), quux(), baz() be evaluated
> and what will happen if one of them throws an Exception? Will
> destructors of the cloned object run? When?


In fact, after the initial ZEND_CLONE opcode (which possibly invokes
__clone()), a separate opcode is generated for each
assignment (the newly added ZEND_CLONE_INIT_PROP). This means that foo(),
bar(), quux(), and baz() will be evaluated
in this very order. If any of them throws an exception, evaluation stops
and the assignments are not rolled back.

Regarding the destructors: yes, the destructor of the cloned object runs
immediately. In order to make sure, I've just added a test case:
https://github.com/php/php-src/pull/9497/commits/4d184f960ac1b5590d87739ee3278c13fac157de
I hope that this result is what
you expect.

Michal wrote:

> Just noticed the "Property name expressions" and am wondering if it could
> be a separate feature
> allowing for passing named arguments to functions/constructors in the same
> fashion?


As far as I can see, Nikita didn't propose the expression1() =>
expression2() syntax in the named params RFC due to
the same ambiguity I mentioned in the RFC (identifier vs. global constant).
But I don't think this is set in stone, however,
I do think that some optimizations would have to be disabled when param
names weren't evaluatable in
compile-time (https://github.com/php/php-src/pull/10831).

Andreas wrote:

What about argument unpacking?
> I don't know if we can combine this with ":" syntax or only with "=>".


For now, argument unpacking (property unpacking?) is not possible. But it
is definitely something that could be added in the future.

Tim wrote:

I'd rather see only the fat-arrow being allowed. Unless I'm missing
> something, braces with colon is not used anywhere else, whereas braces +
> '=>' is known from match() and '=>' more generally is already used with
> array literals [1]. Having two similar syntaxes for the same thing is
> not great when both are commonly needed is not great. They need to be
> documented and learned by developers.


I can only repeat what Rowan answered, since I agree with it completely:

I think it makes sense to have an unquoted form here, because the common
> case is that they are names which analysers can match statically to
> particular properties, not strings which will be analysed at runtime. There
> are plenty of places in the language where dynamic names are allowed, but
> we don't just use strings for the static case


However, I'm not completely sold on making "clone with" look like a
function call (return clone $this with (a: 1);), but
at least I like it more than using an array-like style (return clone $this
with [a: 1];). My intention with
using curly brackets (return clone $this with {a: 1};) is to highlight the
fact that it is a map
of key-value pairs, similarly how the JSON standard does so. Not to mention
that "clone with" serves a very
similar purpose to object initializers, and the different languages I know
to have this feature use
a similar syntax (Java: http://wiki.c2.com/?DoubleBraceInitialization, C#:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers
).

Regards,
Máté

---

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