I've read all the arguments about static typing, but I still can't comprehend how people get themselves into a situation where using the wrong type is a problem. I totally understand it as an optimization if your language supports it, and even for generating docs. I just don't know why people insist on writing code to stop themselves and other developers from doing things.
That said, I really really can't stand breaking backwards compatibility, and it sounds like that is what is happening here.
In a larger codebase, there's two major kinds of value you can get out of types:
- Types act as a contract between component surfaces - if you need to integrate multiple components and have different people working on it, having the compiler enforce the contract is super useful.
- Types act as a refactoring aid - if you need to alter a component, the compiler can assist you in telling you what needs to be changed and you can offload more of that work at compile time rather than at run time.
As pieces move and change, tracking down what else needs to get moved and changed gets more and more difficult and this is where the "passing the wrong type in" issue can occur: having the compiler help you out with that is a huge boon.
Similar to your first point, types also allow an IDE to inform user about what values can be used for a function or class argument.
I can't say that typing bugs occurred very often in released code I wrote before type annotations, but it was not uncommon to encounter them when testing code I had just written (where they usually just caused an exception early on in the program's lifecycle).
If it were a compiled language where I had to wait to run it, I could see that being a big help, but in python I just constantly run the parts of the code I'm editing. If there are any typing issues I catch them almost immediately.
To be fair, I do set return types in my docstrings, and let the IDE help there.
My gripe really stems from using a libraries that check for a type and raise an exception if it doesn't match. Instead of just trying to actually use it and see if that fails. for example If you require a dict because you're going to expect certain keys, but I have a class that has __get_item__ for those keys, it should work fine. If you check the type then you'll fail before you get that far.
Imagine you need to change the type that's passed to a function. You attempt to update all places, but you miss a path that's in an edge case.
Static typing would have caught that for you. By waiting until runtime, you've now just potentially shipped a regression.
As a code base gets large, it's just not possible/practical to run all possible ways the code you're editing can be reached. Unless you happen to be lucky enough to have 100% unit test coverage, which very few people do, and running that test suite is somehow quick enough to be a practical alternative to static typing.
If I need to change a type thats passed to a function that already exists and is heavily used, I would either cast the old type to the new type so both work, or create a new function. If I really cared about cleaning it up I would add a deprecation warning when its done the old way.
The big benefit of type hints things like Pydantic (and some things I've written myself) is that it can provide useful runtime information. If you know that property `obj.foo` is hinted with `List[Foo]`, then that allows you to do something expecting to have a list of `Foo` objects. This is really extraordinary for deserializing untyped data (like a json blob) into appropriate objects at runtime. You can take something like `{"foo": [...]}` and attempt to deserialize each element of the `foo` list into `Foo` objects, rather than just guessing at what the list contains. If you stop being able to understand type hints at runtime, then the only thing they're good for is documentation and static analysis.
If you have to consume functions called call_some_function(), you've got way bigger problems than untyped parameters.
A well designed function/class will have a descriptive name and descriptive parameters, which should give you good hints about their usage. And if it that isn't enough, a quick scan of the code will show you exactly how the function works - a good idea (even in typed languages) if you have any doubts about what you're calling.
The fact that people can program effectively without types is demonstrated by the enormous volume of code in non-typed languages.
So how do I know if a function will return a list or a string? Will it be "get_things_list" ? Or maybe 'get_things', it's got an s, so therefor it's many things? Or at least an iterable thing? Or maybe an object that contains some values? If there are many things can I index into it, or just iterate over it? If I have to guess, will that force me to use a List over an Iterable, even if that's the wrong abstraction?
At some point you have to convey the type or I have no idea wtf you're giving me, and I'd rather have you convey the type in a way my computer can understand than have you convey it through the godawful way that people name things.
That people manage to do so is no endorsement. Humans managed for quite a while without antiseptic but I don't hear anyone endorsing going around with shit on their hands.
Honestly, I've been programming in both typed and untyped languages for ages and it I am asking how you manage this problem, because I find IDEs radically less effective in dynamically typed languages, I find that I have to ask more questions, do more manual work, etc. In typed languages these things become trivial.
A) I think call_some_function() is metasyntactic, not literal. But even if it was...
B) I largely agree but I still don't get how people manage without types, though I get why it occurs. People have different brain styles and hence different programming styles. Personally I dislike dealing with untyped code with "descriptive" names, because it's rarely descriptive enough.
I'd take a strong type system with methods like .call(), .string(), .get(), .issue() with an IDE vs overly-descriptive methods like .frob_the_snozzberries() and .squelch_parrots() any day. It's not composable, it's not predictable - wait was it .frob_snozzberries()? There's no symmetry. That style usually accompanies very dynamic code, since if your interface is very specific, .post() only can mean one thing.
I feel the same when writing a script or small project. Easy right? Types get in the way.
If you feel that way all the time however, it likely means you've never worked on a large or venerable project with multiple developers of varying expertise and skills. Or demanding management. Preventing bugs is exactly what you want to be doing in that situation.
Type checking is like wearing a seat belt, it's not needed a lot of the time, but when it is it'll save yor life.
What do you mean by "algebraic errors"? The problem I (and DDG, and Google) consider that term to mean happens just as often in statically typed languages.
That said, I really really can't stand breaking backwards compatibility, and it sounds like that is what is happening here.