Taking this gradual typing tool a step further, you can run your tests with the typeguard plugin for pytest, which will dynamically check the types of most values, pointing out places where your types are a lie.
https://typeguard.readthedocs.io/en/latest/userguide.html#us...
What you point out is a developer error. Mixing variable types in a dynamically typed language is, like generics, a code smell.
Using a dynamically typed language is all about not having to babysit the compiler, its not about playing dumb about types.
If you design your functions in a way that that function will never receive an array, you dont have to test for it. And if some idiot tries one day, it should error very loudly that something is wrong.
Too many abuse dynamic typing to the point they feel safer offloading this headwork to a compiler.
That is, they get the compiler to make sure nobody will ever send an array into that function that takes a string.
All generics do is bring back the lack of typesafety and all the manual typechecking that comes with muddying generalizing the parameter typeset.
I actually dont hate generics, they are super powerful, but I question every usage as suspicious.
I also wish people would remember human brains arent all the same. We have different ways of thinking about problems. Some people need to have a compiler to busy work, some people need to do busy work while they think.
I just wish people shitting on dynamic typed languages would remember they came to solve the mess and monotomy that arose from typesafety.
Someone mentioned they felt go was going backwards from rust, i think both languages are massive steps backward from python.
Then you've moved it from a test to a runtime assertion. You're still re-implementing the type-checker, poorly.
A compile time error is a caught error. A run time error may be, or perhaps not.
A statically typed language with compile-time type checking would flag that immediately, and chances are your IDE will show the error before it even gets to that point.
Type hints are a thing, but then you are already moving in that direction while remaining in python.
A certain amount of it is probably a familiarity thing, but I do usually find myself able to parse it to the point of what line in which file things started to go wrong.
Besides that, i only said it was a code smell, i dont debate that its useful sometimes. Just that its a signal to be careful, check you really need itor there is t some other constraint you can break.
My only advice is that narrowing your parameter types at the earliest opportunity is a good practice and results in much easier to understand code.
But perhaps not enough.
"Convenience constructors" (i.e., static methods for creating instances from a set of parameters) are fine, but should often not be in the same module (or perhaps even the same package) as the class anyway. For example, if you have a Book class and it has a convenience constructor for creating a book from a database connection string and a book ID, this is probably reasonable for certain applications, but if it's likely that others would want to use your book class in non-database applications, then it would be a travesty to make the non-database application take a dependency on sqlalchemy, a database driver, etc (especially since database drivers tend to be C-extensions which may or may not be difficult to install on various target platforms).
I can assure you I do beleive they have their purpose and place in the world where raw performance or closer to metal abstractions can take place.
Personally, my brain much prefers to use a dynamic type system to infer what i mean leaving the thinking part up to me.
I just find defining a string is a string when im only ever going to put a string in there kind of redundant.
There are other arguments about readability, refactorability, and similar and I think they have some weight in large doverse systems with many many teams interacting.
Personally, that isnt a large issue for me. Im working at the scope I can maintain a standard and a level of consitency that alleviates these problems to the point the type safety is simply not wareanted from a return on investment point of view.
I do not, for instance, think the kernel or chrome should be rewritten in javascript. Despite what some react developers seem to think.
There are 2 problems with this kind of thinking.
1) you aren't going to be so sure. Humans are imperfect and as codebase grows, everyone commits such errors. Eg comparing a string to int without converting it to int in python. It always returns false (IIRC) and it is hard to detect where the problem is.
2) Most type systems aren't that verbose. I guess you got the impression from java / mediaeval C++. But most statically typed languages have local variable type inference. OCaml / Haskell / F# / Swift etc.. have varying degrees of type inference. At this point, types in method signatures, in languages that require it, serve as documentation. And many of these languages are terser than python/javascript.
1) I work on a typescript codebase for work. There is a real need for types in this case because the definitions are about 3 layers of abstraction away from their use. It drives me up the wall because I know when you keep definitions close to use, or have some basic conventions this problem melts away. At least to the point of diminishing returns where types are no longer relevant.
So, i guess over the years ive been caught out by int + string = 0 all of like, 1-2 times in production, maybe a few more while in dev. Either way its rare enough for me to call FUD on the argument. Stop mixing types and this really doesnt happen. To be fair to your point, at work, i know of a recurring error in production where this is almost certainly the same class of error. Nobody can be bothered fixing it because its too hard to track down and the cost benefit isnt there.
2) Type inference is problematic for me. Doesnt it just open you up to type errors of a different class?
2.5) terse code is not the point, I find rust cryptic and dense to say the least. I prefer something like go wgere readability is a priority over density.
It's at compile time, so not really? It will tell you exactly what it expects vs what you provided at compile time. These kind of languages read like dynamic languages (for the most part) but they are completely static.
I'm not sure if I'm misunderstanding you here, but generics are type safe. They're statically checked at compile-time.
That's naïve. Unless you code extra defensively (which is the same type of overhead as excessive testing, and needs itself to be verified by testing), there's very little guarantee that type errors will result in loud failure consistently at the first point that the bad value is passed (because functions that don't do unnecessary work often pass their arguments along without doing a lot of work dependent on the very specific type.)
And, in any case, failing consistently at compile time (or, with a modern code editor, at writing time) with static type checking is better than failing at runtime, even if it is loud and consistent.
Of course things like mypy and TypeScript (especially, in the latter case, because it powers tools that work pretty well when the actual immediate source is plain JS, or JS with JSDoc comments from which type information can be extracted, when consuming libraries with TS typings) mean that some popular dynamic languages have static type checking available with more expressive type systems than a number of popular statically-typed languages.
def foo(arg: str) -> int
vs. def foo(arg: List[Union[Set[int],Optional[Dict[str, Any]]]]) -> str
But...Python has a static type checker with a more expressive type system than Go.Also, IIRC, better type inference.
In my view, type checking lulls one into thinking the code is better than it actually is, just because it compiles. But it doesn't test the algorithms. And testing the algorithms exercises the code enough to reveal the shallow type errors.
But yes, there's a lull with type checking that makes people believe only negative testing is sufficient, which is as foolish as thinking only positive testing is sufficient.