On Wed, Dec 1, 2021 at 9:09 PM Steven D'Aprano <st...@pearwood.info> wrote:
>
> On Wed, Dec 01, 2021 at 07:07:20PM +1100, Chris Angelico wrote:
>
> > >     def process(func:List->int=>xs=>expression)->int:
> > >         ...
>
> > I'm not sure what that's supposed to mean.
>
> You did a pretty good job of working it out :-)

Okay, cool, thanks.

> Exactly my point. Beyond your arrow syntax for default parameters, and
> the existing return annotation use, there are two other hypothetical
> proposals for arrow syntax on the table:
>
> - using `->` as an alias for typing.Callable;
>
> - using `=>` as a more compact lambda;

That confuses me. Why would they use different arrows?

If we have a compact lambda syntax, why can't actual functions be used
as annotations to represent functions? I believe there's plans to have
[str] mean List[str], which makes a lot of sense. Why not have "lambda
str: int" or "str=>int" be an annotation too?

But okay. Supposing that annotations have to use one arrow and lambda
functions use another, then yes, this is a good reason to use "=:" for
late-bound defaults. It's not that big a change.

> > func:ann=>dflt # late-bound default, completely unnecessary here
>
> Come on Chris, how can you say that it is "completely unnecessary"?
> Unless that's an admission that late-bound defaults are all
> unnecessary... *wink*
>
> There is a difference between these two:
>
>     def func(arg=lambda a: expression):
>         ...
>
> and this:
>
>     def func(arg=None):
>         if arg is None:
>             arg = lambda a: expression
>         ...
>
> therefore there will be a difference between:
>
>     def func(arg=lambda a: expression):
>     def func(arg=>lambda a: expression):
>
> If nothing else, in the first case (early binding) you get the same
> function object every time. In the second, you get a freshly made
> function object each time. Since function objects are mutable (they have
> a writable `__dict__` that's a visible difference even if the bodies are
> identical. And they may not be.

Yyyyyes. Okay. There is a technical way in which you might want this
alternate behaviour. Is that REALLY something you're planning on doing
- having a function which takes another function as an argument, and
which behaves differently based on whether it's given the same
function every time or multiple different functions with the same
behaviour?

> But even if it is unnecessary, it will still be permitted, just as we
> will be able to write:
>
>     # Now this *actually is* totally unnecessary use of late-binding
>     def func(arg=>None):

Yes. That one is absolutely unnecessary, since there is no way you'll
ever get back a different result. Except that you could then mutate
func's dunders and change the behaviour. So in a purely technical
sense, nothing can be called "unnecessary".

Now, in real terms: late-binding a lambda function with no default
arguments seems like a pretty pointless thing to do. I stand by my
original statement :) And I also stand by my original statement that
proper use of the space bar can not only improve readability, it can
also help you find a date with space girls and space guys... or maybe
that last part only works on Mars.

> > xs=>expression # presumably a lambda function
> > def process(args)->int # return value annotation
> >
> > Why should List->int and xs=>expression use different arrows?
>
> Because they do different things. To avoid confusion.
>
> Chris, you wrote the PEP for the walrus operator. Why should the
> assignment operator use a different symbol from the assignment
> statement? Same reason that typing.Callable and lambda will likely use
> different arrows.
>
> Anyway, if you disagree, take it up with Guido, it was his suggestion to
> use different arrows :-P

Fair enough, but typing also started out with List[str] and is now
looking at using list[str] and, I think, [str], because it's more
consistent :)

> > Wouldn't
> > it be much more reasonable for them to use the same one, whichever
> > that be? And if that does turn out to be "=>", then yes, I would be
> > looking at changing PEP 671 to recommend := or =: or something, for
> > clarity (but still an equals sign with one other symbol next to it).
>
> Oh great, now you're going to conflict with walrus...
>
>     def process(obj:Union[T:=something, List[T]]:=func(x:=expression)+x)->T:
>
> We ought to at least try to avoid clear and obvious conflicts between
> new and existing syntax.
>
> Using `:=` is even worse than `=>`, and `=:` is just *begging* to
> confuse newcomers "why do we have THREE different assignment symbols?"

If you look at every way the equals sign is used in Python grammar,
there are a lot of subtle differences. The difference between "f(x=1)"
and "x=1" doesn't bother people, and those are exactly the same
symbol. There are only so many symbols that we can type on everyone's
keyboards.

But you're right, and that's why I would very much like to reuse an
existing symbol rather than create new ones.

> > It's always possible to come up with pathological code. But this is
> > only really as bad as you describe if it has zero spaces in it.
> > Otherwise, it's pretty easy to clarify which parts go where:
> >
> > def process(func: List->int => xs=>expression) -> int:
> >     ...
>
> "My linter complains about spaces around operators! Take them out!"

Yes, take your linter out the back and execute it :)

> Or maybe we should put more in? Spaces are optional.
>
>     def process(func : List -> int => xs => expression) -> int:
>         ...
>
> I'm not saying that an experienced Pythonista who is a careful reader
> can't work out what the meaning is. It's not *ambiguous* syntax to a
> careful reader. But it's *confusing* syntax to somebody who may not be
> as careful and experienced as you, or is coding late at night, or in a
> hurry, or coding while tired and emotional or distracted.
>
> We should prefer to avoid confusable syntax when we can. The interpreter
> can always disambiguate assignment as an expression from assignment as a
> statement, but we still chose to make it easy on the human reader by
> using distinct symbols.
>
>     def process(arg>=expression)
>
> would be totally unambiguous to the intepreter, but I trust you would
> reject that because it reads like "greater than" to the human reader.
>

Actually that, to me, is no different from the other options - it's
still an arrow (but now more like a funnel than an arrow, which is a
bit weird). It's no more a greater-than than "=>" is.

ChrisA
_______________________________________________
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/WFOFQW2XSVBE43LV6CR6RIFXSQ2ABRFQ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to