On Mon, Jul 29, 2019 at 5:35 PM Rowan Collins <rowan.coll...@gmail.com>
wrote:

> On Mon, 29 Jul 2019 at 15:41, Nikita Popov <nikita....@gmail.com> wrote:
>
> > This proposal (at least combined with a declare that enforces use of
> > call-site annotations) addresses that concern. out/inout parameters do
> not
> > address this concern, because they are an mechanism that would be used
> *in
> > addition* to normal by-ref passing, and as such not address any problems
> it
> > has.
> >
> > The only way I see in which out/inout would actually address the concern
> > of this proposal is if out/inout were used instead of & at the call-site,
> > but the parameter were still a normal reference. I don't think that's a
> > good idea because it breaks symmetry between arguments and parameters,
> and
> > also squanders any future potential to use out/inout as a way to reduce
> > reference-use in this context.
> >
> > Does that make sense, Rowan? To put is more compactly, what I want is an
> > eventual state where given $fn($a) I have a guarantee that $a is not
> going
> > to be magically modified, without having to perform any global reasoning
> > about what $fn may refer to. If an alternative proposal does not result
> in
> > this eventual state, then it is not an alternative to this proposal (but
> > possibly still a valuable addition in itself).
> >
>
>
> It does, yes. I guess what I had in mind was that *in the long-term*,
> out/inout parameters would replace & parameters, so rather than
> declare(require_reference_at_call_site=1) you would have
> declare(disable_reference_parameters=1).
>

Something like that could in principle address the problem, though it would
enforce certain design decisions to allow reaching that eventual state. I
think this is how out/inout would have to work in order to satisfy BC
concerns while still allowing a declare-based mode that eliminates plain
by-ref passing entirely:

 * All current by-ref parameters in extensions must be changed to out/inout
parameters.
 * By default, an out/inout parameter will behave exactly like a reference
parameter and there is no behavioral difference between out/inout.
 * Additionally, the call-site is allowed to specify out/inout. If it is
specified, it must match the parameter. If it is specified, inout will
generate a notice if the passed value doesn't exist (while out behaves as
usual reference pass).
 * A declare() exists that would require require call-site out/inout
annotations and prohibit calls with reference parameters (that have not
been converted to out/inout).
 * Additional changes to type-checking for out/inout parameters in userland
functions.

One migration problem that would still exist is that 3rd-party libraries
would also have to migrate to out/inout parameters first, before users
could enable the declare() mode that disables reference-passing --
otherwise they would not be able to make use of some of the library
functionality, or would have to opt-out in parts of the code. Alternatively
one could allow the use of out/inout with normal reference parameters as
well, in which case both out/inout would be allowed at the call-site.

Whether or not it's a direct alternative to this proposal, I also worry
> that out/inout parameters are somewhat mutually exclusive with it: the more
> effort is spent on making reference parameters "better", the less appetite
> there will be for replacing them altogether.


This kind of goes both ways: There is such a thing as "good enough" and
there's also the typical effect that going from a 95% solution to a 99%
solution will often take many times more effort (what's the technical term
for this?) In this context, "effort" isn't even the right word, it's a
combination of initial implementation effort, long-term maintenance cost
and possibly most importantly, language semantic complexity.

Above, I have outlined a way in which out/inout could work while mostly
addressing my original motivation. But is this approach really better than
a simple call-site "&" annotation that flawlessly integrates into the
existing reference passing system? I'm not convinced that it is. Especially
if we need to introduce out/inout in a way that interoperates well with
references for BC/migration purposes, the semantics become fairly complex,
while (imho) not offering a lot of benefit over a simple by-reference pass.
The main benefit I see would be an opportunity to fix the current
interaction of references and type annotations.

Every time I have discussed out/inout with people before, it has been in
the context of "What would we have to do to completely remove references?"
A large part of that is finding an alternative to by-reference passing that
does not use references. Hack introduced inout parameters (see
https://hhvm.com/blog/2018/03/15/hhvm-3.25.html) specifically to avoid the
issues of reference-passing (and they behave essentially like the
desugaring I mentioned in some earlier mail). But we would not get these
benefits using the approach outlined above, and I don't believe we could
use something significantly different while still supporting existing
by-ref internal functions.

This is why I don't think these two things should be mixed. Proper
out/inout parameters should not have anything to do with references.

I agree the behaviour of type
> annotations would need deciding, but arguably that should have been true
> for by-reference parameters; it's somewhat bizarre that you can write
> "function foo(SomeClass &$x) { $x=42; }" The solution would probably be to
> reuse the "typed references" from the typed property implementation.
>
>
>
>
> > Bob has brought up another interesting issue: This proposal currently
> does
> > not address the case of foo(...$a), where references into $a may be added
> > if foo() has by-reference parameters. The suggestion was to use
> foo(&...$a)
> > -- however in this case only to *allow* the use of references, not
> require
> > it (some args may be by-val while others may be by-ref).
> >
>
>
> There's probably a bit of a rabbit-hole if the mission is "make references
> explicit". An array containing a reference can be passed by value, for
> instance, so $fn($a) might not technically modify $a, but still modify
> elements inside it. So it seems like we come back to "this is kind of
> useful information but may not actually offer a hard guarantee".
>

Right, references in arrays and objects are a pretty big WTF factor
(especially in how they behave with copying an cloning), but I don't really
have a good answer to those.

Regards,
Nikita

Reply via email to