On Sat, Jan 08, 2022 at 12:59:38AM +0100, jack.jansen@cwi.nl wrote:
I posted this suggestion earlier in the callable type syntax discussion, at which point it was completely ignored. Possibly because it’s a really stupid idea, but let me post it again on the off chance that it isn’t a stupid idea but was overlooked.
If I can make a wild suggestion: why not create a little language for type specifications?
Any time we are tempted to prefix a question with "Why not ...", the question is backwards. The right question is, "why should we ...". Python is 30 years old and mature, with a HUGE ecosystem of users, libraries, tooling, etc. It is far, far easier to get changes wrong than to get them right, which is why we should be conservative when it comes to language changes (including syntax). Changes to the language are "default deny", not "default accept", and it is up to the proponents of the change to prove their case, not for opponents to disprove it. https://www.curiousefficiency.org/posts/2011/02/status-quo-wins-stalemate.ht... https://www.curiousefficiency.org/posts/2011/04/musings-on-culture-of-python...
If you look at other programming languages you’ll see that the “type definition sub-language” is often completely different from the “execution sub-language” [...]
Yes, and that makes other programming languages harder to use and harder to learn. Even Pascal, which has a nice human-readable declaration syntax, requires you to learn more before you can do anything. C is particularly bad, since there are effectively three distinct languages to learn: macros, type declarations, and C code. Anyone who has learned enough C to get good at it is likely to under- estimate how hard C is to learn, due to survivorship bias (those who have survived their apprenticeship as a newbie C coder to become an expert are not representative of all newbie C coders) and the curse of knowledge (experts often find it hard to put themselves in the shoes of non-experts). So I think that we should avoid the temptation to have a distinct language for type annotations. I think that Guido's initial reasoning, that type annotations are Python expressions, rather than distinct syntax for declarations, was correct, and I think that the Steering Council is right to insist on keeping annotations aligned as Python code. Mind you, that doesn't necessarily mean that we cannot introduce new syntax for use in annotations. (Just as we added three-argument slicing and Ellipsis specifically for use in Numpy.) So long as they are usable outside of annotations, they remain "part of the Python language" rather than a distinct "type declaration syntax". For example, the arrow syntax for Callable `(int) -> str` (if accepted) could be a plain old Python expression, usable anywhere the plain old Python expression `Callable[[int], str]` would be.
Python typing uses basically a subset of the execution expression syntax as its declarative typing language.
I think a good way of putting it is that the typing module defines a little DSL (Domain Specific Language) for type hints, but annotations themselves are just Python expressions.
What if we created a little language that is clearly flagged, for example as t”….” or t’….’? Then we could simply define the typestring language to be readable, so you could indeed say t”(int, str) -> bool”. And we could even allow escapes (similar to f-strings) so that the previous expression could also be specified, if you really wanted to, as t”{typing.Callable[[int, str], bool}”.
The following are not rhetorical questions. I don't know the answers, which is why I am asking. 1. Are these t-strings only usable inside annotations, or are they expressions that are usable everywhere? 2. If only usable inside annotations, why bother with the extra prefix t" and suffix "? What benefit do they give versus just the rule "annotations use this syntax"? 3. If usable outside of annotations, what runtime effect do they have? The t-string must evaluate to an object. What object? 4. If the syntax allowed inside the t-string is specified as part of the Python language definition, why do we need the prefix and suffix? E.g. if we can write: # Equivalent to T = Callable[[int], str] T = t"(int) -> str" and have the arrow syntax defined by the language, then surely the prefix and suffix is redundant. Likewise, if this is allowed: def func(arr: t"array [1...10] of int") -> str: ... why not just allow this? def func(arr: array [1...10] of int) -> str: ... 5. What difference, if any, is there between `t"{expression}"` and `expression`? If there is no difference, then I don't think that the t-string syntax adds anything to this proposal. Remove the t-string syntax, and just write the type expression. This is not the case with f-strings, where they actually do add something to the code: `f"{expr}"` is not the same as `expr`. -- Steve