On Fri, Feb 21, 2020 at 11:20 PM Rowan Tommins <rowan.coll...@gmail.com>
wrote:

> On 20 February 2020 14:13:58 GMT+00:00, Nikita Popov <nikita....@gmail.com>
> wrote:
> >Hi internals,
> >
> >I'd like to start the discussion on the "explicit call-site
> >pass-by-reference" RFC again:
> >https://wiki.php.net/rfc/explicit_send_by_ref
>
>
> Hi Nikita,
>
> Thanks for putting the case for this so clearly. My instinctive reaction
> is still one of frustration that the pain of removing call-site ampersands
> was in vain, and I will now be asked to put most of them back in. It's also
> relevant that users already find where & should and should not be used very
> confusing. There is a potential "PR" cost of this change that should be
> weighed against the advantages.
>
> I'm also not very keen on internal functions being able to do things that
> can't be replicated on userland, and this RFC adds two: additional
> behaviour for existing "prefer-ref" arguments, and new "prefer-value"
> arguments.
>

I should say that this is a non-essential part of the RFC. I noticed that
this RFC provides a way to solve this problem, but if we don't think it the
problem is worth solving, then we don't have to solve it.

The prefer-ref/prefer-val thing is indeed a bit peculiar. It's an artifact
of the current way of implicit by-reference passing, where the decision of
whether to pass by-value or by-reference has to be made based on an
"educated guess" at the call-site. That leaves us with always-val,
always-ref, prefer-val and prefer-ref as the possible passing modes. In the
explicit by-ref passing regime, the latter two consolidate, and we have
by-val, by-ref and "either" as the options, which is a lot more obvious.

But again, I can't say I'm fully convinced myself that this is really a
problem we need to solve. I don't really care about call_user_func() at all
(it is entirely obsoleted by $fn()), and now that I think about it,
__call() isn't really the right primitive to expose anyway. If you will
allow me a little digression...

Instead of having __call(), what we really should have is __get_method().
For a simple forwarding proxy, the implementation would look something like
this:

public function __get_method(string $name): Closure {
    if (method_exists($this->proxy, $name)) {
        return Closure::fromCallable([$this->proxy, $name]);
    }
    return null;
}

This solves multiple problems with one stone: First, it preserves the
signature of the method we're proxying to: This is better than the solution
in this RFC, because it preserves both by-ref argument passing and by-ref
returns, and can validate that properly (i.e. passing a non-ref to by-ref
will diagnose). Second, it makes is_callable() work precisely, because we
no longer have to assume that with __call() any method is callable. Third,
it makes Reflection work on the proxied method.

It's possible to recover normal __call() semantics from this approach by
writing something like this:

public function __get_method(string $name): Closure {
    return function(...$args) use($name) {
        // Normal __call() implementation in here.
    };
}

My current opinion is that I'd rather wait for the details of out and inout
> parameters to be worked out, and reap higher gains for the same cost. For
> instance, if preg_match could mark $matches as "out", I'd be more happy to
> run in a mode where I needed to add a call-site keyword.
>

I believe we talked about this in some detail in the previous discussion on
this topic. My basic stance on in/out is that it's *probably* not worth the
complexity, unless it is part of an effort to eliminate references from PHP
entirely (which would be hugely beneficial). Unfortunately I don't really
see a clear pathway towards that. "out" parameters can remove one use-case
of references, and I can see how that would work both in terms of semantics
and implementation. The case of "inout" parameters is much more
problematic. While these can nominally work without references, I don't see
how they could do so efficiently (we would have to "move out" the value
from the original location to avoid COW). Similarly, I don't have any
answer to how &__get() and &offsetGet() would work without references.

Regards,
Nikita

Reply via email to