> > > What about using a real closure to define the scope we need for
> cloning?
> > > That closure would take the cloned instance as argument to allow
> > > manipulating it at will.
> >
> > I believe someone mentioned that one previously in the thread.
>
>
> Yes, Nicolas mentioned me.
> I wanted to get back myself to discussing this topic more as well and find
> a better solution but didn't get yet time for it.
>
>
> >   The problem is that the closure would run in the scope of the object,
> > not the scope of the caller.  That means if called outside the object, it
> > would allow modifying private or protected properties.  The itemized list
> > of values (whether an array or named-args style) would allow the engine
> to
> > enforce access restrictions, which is a desireable feature.
> >
>
> As far as I can see, Nicolas was able to find a solution to this problem
> and so far I like it:
> The closure is running in the current scope where it is defined and not in
> the scope of the cloned object. The cloned object is passed as the single
> parameter to the closure.
>

Absolutely. This would be a plain boring closure with all its current
visibility semantics.
Using a closure to run some code nested in a transaction is already quite a
common practice.
E.g.this is a common way to define the computation logic for a cache
storage:
$cache->get($cacheKey, $callback)

Or for a database transaction:
$db->transaction(function() { ... });

The cloning logic we want to run fits this style, so this is quite natural
once we realize that:
$clone = clone($this, $callback);

The only thing we would need to settle on is the interface of the $callback.

The suggested clone signature for a class T would be:
> - clone(T $object, callable(T $clone)): T; // calling clone as a function
> - clone T $object with callable(T $clone): T; // calling clone as a
> language construct


100% this. My preference goes for #1, to keep things as boring as possible.
Just to make it clear, I would document the closure with the void return
type:
clone(T $object, callable(T $clone)*:void*): T; // calling clone as a
function


> Alternatively, we can have also:
> - clone T, callable(T $clone); // without "with" keyword
>

This would be ambiguous, eg foo(clone T, callable(T $clone)) is that a
function call with two arguments, or?


> And improve it to allow even multiple closures to be executed:
> - clone(T $object, ...callable(T $clone)): T;
> - clone T $object, ...callable(T $clone): T;
>

I wouldn't allow this. Calling many closures is what the main closure can
do in its body, no need for more fancy things.

Nicolas

Reply via email to