As a “typing skeptic”, I share Paul’s concerns here. I’m still not totally sure whether there are any changes being proposed to the language (adding str.chars
would be a change, but yes, backward compatible). But It does seem that there is a proposal to change something in the stdlib typing module, yes?
Strictly speaking, it would only affect those that choose to use typing. But the fact is that typing is creeping into the Python ecosystem a LOT now – to the point where (I know from experience) many newbies think it’s recommended, if not actually required. And some of those folks very quickly start changing their code to make the type checker happy, rather than changing the type annotations.
All that is about messaging and education – but in regard to this thread, the point is that folks that are not strictly needing to do, or choosing to do, typing will be affected. And it’s not just newbies - maybe I’m confused, but this conversation seems to be focused on use cases within a system: i.e. where the same person (group) is writing both the type annotations and the calling code. But for libraries with type annotations, the users of those libraries should be able to ignore the distinction between an “atomic string” and an “iterable string” – I’m still not sure if that’s the case with this proposal.
Python is a highly dynamic language that we are adding optional static type checking to – the type checkers should contort themselves to match the language, not the other way around. Which means, as awkward as it might be, rather than essentially changing what type checkers think a str
can do, the typing system should have a way to express:
“An iterable of str
that is not a str
”
Rather than re-defining what a str
is and adding “a ‘str’ that is also an iterable”
Yes, I know that “An iterable of str
that is not a str
” is far more commonly needed than “a str
that is also an iterable”, but the str
is and has always been iterable, which is why I think the typing systems should adapt themselves to what’s already there.
Heck, maybe a ugly hack, but could Iterable[str]
be special cased by the type checkers to reject str by default? That’s the actual problem isn’t it? I understand that Iterable[str]
is not strictly a single type – but couldn’t we pretend that it was?
That would be an interesting question to look into – in the pytype user community (and everywhere, I guess) – how often do they want a “non iterable string” other than in the context of Iterable[str]
?
I guess what all this boils down to is that there IS a “typing problem” – but the problem is with Iterable[str]
, not with str
itself – adding a static type system on top of a dynamic language is going to be messy now and again.
PS: please tell me what FM to R to answer this, but I’m confused as to what the problem is with Paul’s example:
def uppercase_and_vowel_count(s: str) -> tuple[str, int]:
vowels = sum(1 for ch in s if ch in "aeiou")
return s.upper(), vowels
Even if str
is interpreted as non-iterable by type checkers, wouldn’t the type checker simply check that you specified str
, and the caller is passing a str
, and you’re all set?
I can see that this would mess up static type analysis – the code in the function couldn’t be correctly analyzed to know what type s is supposed to be – but it doesn’t have to, it’s been specified. Are there tools that check the actual code to see if it’s type-correct? And if so, couldn’t that tool be special cased to know that when you specify an actual string, you do mean an actual string?
Side note: I have used iteration through strings a LOT – but as I think about it, that’s because I use it in a lot of toy examples when teaching Python – I’m not sure I’ve used it in production code ever – the string methods provide most of the functionality I might use that for out of the box.