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/