TL;DR: declaration only syntax is a non-starter. Even if we tried to add specialised syntax that only appears in annotations, it would become a regular Python expression almost immediately. On Sun, Jan 09, 2022 at 08:42:14AM -0800, Christopher Barker wrote:
Is it more clear for readers to have two different (but related) related syntaxes for two different (but related) purposes, or to have one Sytax that is used in different ways?
That is not a valid "either/or" choice. The choice is: * Python expressions that have multiple meanings (the status quo); (e.g. subscripting `obj[x]` already had 4+ meanings before its use in typing) * or Python expressions that have multiple meanings, PLUS new declaration syntax that is only valid in annotations. And I will argue below that the culture of Python is such that the second choice is practically *impossible* without a radical shift in the culture. Let's say that we decide to add specialist declaration-only syntax for type-hints. What next? First, we need a way to distinguish between type-hints that are declarations from type-hints that are expressions. Let's say that we use the proposed t-string syntax: spam: t"declaration goes here" That tells the interpreter to treat everything inside the quotes as a pure declaration, not an expression, with no runtime effect. I'm going to put aside the question of what syntax is allowed for the declarations inside the quotes, because that's actually not that important. What happens after we add this declaration syntax? The first thing that happens is that people interested in runtime processing of annotations will want to inspect the declaration, without having to scan the source code (which might not be available). Python has a strong culture of runtime introspection, starting with dir() way back in Python 1.x days, so if you think the core devs are going to resist the call to make those annotations visible at runtime, I think you are remarkably ~~wrong~~ optimistic :-) That means that the declaration has to have a runtime effect. Something has to go into the `__annotations__` dict: __annotations__['spam'] = ... # what goes here? (It doesn't matter precisely what the value that goes into the mapping is, it could be a string, or a type object, whatever it is, it is a value.) So our declaration is no longer a declaration, like `global`, it is an expression that returns a value. The parser merely restricts its use to annotations like `spam: t"declaration"`. Why can't we use that nifty new declaration syntax in type aliases? I mean, it already evaluates to a value that can be inspected at runtime, so we just have to relax the restriction on where it can appear so we can use it in type aliases as well as directly in annotations: T = t"declaration goes here" spam: T|None But if we can do that, then our declaration is just another expression. It has a value, it can appear outside of annotations. People are going to want to treat it as a first-class value (no pun intended), even if only for testing purposes: # Unit tests for T in [t"declaration", t"another declaration", t"yet another", t"my hovercraft is full of eels"]: self.assertIsInstance(T, typing.Generic) ... Even if it doesn't happen from day 1, rapidly whatever restrictions on where the syntax is allowed will be relaxed, because that's the sort of language Python is. Once we make that first (and inevitable!) step to give these type declarations an introspectable value, then they will become first-class expressions as sure as death and taxes. So I maintain that *declaration only syntax* is doomed. Whatever syntax we invent, there is going to be irresistable pressure to make it a first-class expression and not just restrict it to annotations. And if that is the case, then why do we need the t-string quotes? That's not a rhetorical question. Something like t-string quotes, or some other pair of delimiters, *may* be useful if we have our hearts set on syntax which is radically different from regular Python expressions: spam: t"array 1 to 50 of int" # Hybrid of Pascal and Hyperscript eggs: t"{(+⌿⍵)÷≢⍵}" # I don't even... Just as we have `[ ... ]` delimiters for list comprehensions. Maybe it turns out that there are cases where we need delimiters. But if so, I hope they aren't quotation marks (with or without the t-prefix), since that will be confusing as far as the existing use of strings as forward references. But why do we want type hints to be so radically different from regular Python expressions? How does that make typing easier to read and the language less complicated, if we have to learn *two* languages to be fluent in Python instead of one? Outside of that, the t-string delimiters are redundant. There is no need to wrap the proposed arrow syntax in quotes, as the PEP makes clear they can be handled just fine as an expression. The arrow expression doesn't need extra delimiters, it adds nothing. They're just noise.
If "x->y" is syntactically valid anywhere in Python code, it's not a problem that there are no core data types for which it's meaningful.)
Here's where I'm not so sure -- this looks a lot like a binary operator, but it behaves quite differently.
How is it different? Aside from the syntax requirement that the left operand is parenthesized, we can treat it as an operator: (one of more input args) arrow operator return-arg Whether the parser treats it as a binary operator or something different, like the dot name.expression, isn't really important. It is a symbol that has a left-operand and a right-operand, using infix syntax. I call that an operator, at least in informal language. There's no dunder for the arrow, just like `is`, `is not`, `or`, `and`, `not`, so no operator overloading. Just like `is` etc. They are still operators. And just like other operators, if you pass the wrong input types, it will raise a TypeError. -- Steve