I'd love to squash this conversation (because I don't think it'll lead
anywhere) but you've nerd-sniped me a little bit.

In 3.8, foo(a := 42) is in fact valid syntax, meaning the same as foo((a :=
42)).

If you wanted to do this with explicit syntax, you can just pass any
mutable object. We have a convention in Python 2 (which doesn't have
nonlocal) for setting a = [None] in an outer scope and then in the inner
scope assigning a[0] = <value>. This can be done for function arguments
too. In this case you would be right that the initial value of the variable
is constrained.

You can come up with new syntax in 'def' that make this transparent inside
the function (e.g. your 'def fun(out := outvar)') but in the caller it will
require special syntax because the Python compiler can't know the signature
of the called function (this is not a limitation of the CPython bytecode
compiler but a feature of the language).

We could probably come up with vaguely acceptable syntax similar to C,
making the caller write foo(&a). (We could even make this introduce 'a' as
a local variable if it wasn't already known.) This would be a runtime type
error if foo() doesn't have an output parameter in that position.

But I don't think the use case is as strong as the OP believes it is.

--Guido

On Mon, Aug 26, 2019 at 10:02 AM Andrew Barnert <abarn...@yahoo.com> wrote:

> On Aug 26, 2019, at 08:38, Guido van Rossum <gu...@python.org> wrote:
>
> Sorry, there is no way to leverage the implementation or syntax of := for
> this purpose. You must be misunderstanding how they it works.
>
> You are right that the initial value of the variable at the call site is
> irrelevant (but there must be some initial value, e.g. None, to be able to
> identify the variable).
>
>
> I’m not sure this is quite true.
>
> I do think it would be an abuse of := syntax, and any design would be hard
> to read and teach, but I don’t think it would be impossible to implement.
>
> With traditional pass by reference, you need some kind of dereferencing
> syntax—but once you have that, the “output” variables can be uninitialized.
> Consider this C-style pattern (which you see all over the place in ObjC):
>
>     int func(Error* error = NULL) {
>         if (error) { *error = MyError; }
>     }
>
> The actual out variable is *error, not error, and it’s optional, and it
> can be passed uninitialized.
>
> But I think the OP is looking for something more magical, where the
> function definition just sees the Error rather than the Error*, and can
> just assign to error (which, under the covers, assigns to *error with the
> null check). That isn’t that hard to add to a C-style language—and it
> actually fits better into Python, where there is no explicit dereferencing
> anywhere. And you can still “pass” an uninitialized variable or leave it
> out, because you’re not really passing anything, you’re just giving a name
> for the machinery to bind at the end (which is just a normal assignment).
>
> To make sure I understand the proposal, let me give a simplified but
> complete use case so the OP can tell me it I got it wrong:
>
>     def fun(out := outvar):
>         outvar = 3
>
> This isn’t valid syntax today; a := expression can appear in a
> default-value expression or a type annotation, but it can’t appear directly
> as a parameter in a parameter list. So, this would be entirely new syntax
> that could be made legal.
>
> And, once you do that, you can assign entirely new semantics.
>
> This doesn’t assign a value to out, or even make it a local variable name;
> out only exists for the calling machinery to get the name to bind.
>
> It does make outvar a local variable name (adds it to the list of local
> names in the code object), but that still doesn’t have to give it a
> value. It’s like having “if False: outvar = None” at the top of the body.
>
> What about on the call side? Here I’m not quite as sure, but I don’t think
> you can have a := expression in an argument list without wrapping it in
> parens? If so, then this is also new syntax:
>
>     fun(myvar:=out)
>
> If I’m wrong, then you’d need some other syntax to say “pass myvar as a
> keyword argument to the out parameter, but pass by name/by
> reference/whatever”. (You can’t rely on the fact that out was special in
> the def, because you can’t see what’s being called; it has to be something
> in the call syntax.) But I’m sure you could invent something.
>
> And whatever the syntax is, the semantics don’t require any existing
> value in myvar.
>
> So, how do you implement this? If you want intermediate assigns to be
> available even if you exited by exception, you’d probably have to use
> cells. But I think most languages with explicit outvar syntax don’t
> actually assign back until the function returns normally. And that’s easy.
> Within the function, outvar is just a plain old local. The RETURN_VALUE
> code sees that the code object has N outvars, so it actually returns a
> tuple of (real_return_value, *outvars). (This is an UnboundLocalError if
> any of them didn’t get assigned to, even if they don’t get bound to caller
> names.) Then, on the caller side, the call was just compiled to a
> CALL_FUNCTION and then a tuple extraction and a bunch of STORE_* ops
> (ignoring any outvars that weren’t bound).
>
> But, as I said at the top, I think this would be hard to teach and learn,
> and confusing to read even after you’d learned it. And returning a
> namedtuple (or taking a SimpleNamespace to fill, or just making it a
> mutating method of an object…) does seem like a much better way to solve
> the UX problem, as you said.
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him/his **(why is my pronoun here?)*
<http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/URPYG4PUYLW3AAFBKJTFQQISGR47FVZ4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to