`cargo` combines poetry, ruff, pipx, sphinx, pytest, etc. You might expect this to be worse. In some cases, it is (e.g. pytest). The super power is in being able to get a project up and going immediately without wasting time on figuring out which tools to use, scaffolding them together, setting up a host for your documentation, etc.
Then there comes the problem of distributing it where the target system has to have just the right version of Python.
I have been working on a mediumish project (without tests or type hints) where I needed to add functionality including a bit rewiring.
My main problems were:
- I didn't know what data (types) were passed around when looking at parts of the code, so I had to look were the data structures were created, adapted and passed along to actually know more (or just debug it)
- After changing parts of the code, I was 100% certain that I had to run it a dozen times to fix new bugs or things I had forgotten
How it works with Rust:
- I see the types in the function signature
- I know the properties of 'classes' without scanning the whole code of the class
- when it compiles, it works 90+% of the time, because the compiler tells me where I forgot something before, if the IDE hasn't already told me
For me this is the big one. Types isn't just about writing error-free code, it also aids in making the code self-documenting.
When I read a function signature in an unknown codebase in C, C++, C#, Delphi or similar, I can almost always tell what it does and what I can do (or not). Even types I haven't seen before helps, because the name of the types can be informative.
Thanks to this I've helped countless people fix their code using libraries I've never seen before in mere minutes, simply by glossing over some of the code.
When I work in untyped code, like older Python or similar, it's much harder. Invariably I end up sprinkling dir() or similar just to figure out what the heck is going on. Ok, this so function created... something... what can do with it? Who knows?
str vs bytes confusion is a major example. If you use one where you needed the other, the results can be anywhere from inscrutable failures to hilarious. My personal favorite is if you stick bytes into a templating engine. For some reason, iterating bytes in Python yields integers (not bytes-of-length-1 or a separate byte type), and those integers can get stringized and flattened, and your webpage turns into a bunch of digits and you end up with the Matrix.
You will be amazed to find that that's how it works with C, Pascal, Java ...
Types being declared in function headers and checked is 1960's technology.
For example, a c header type might have an out parameter and an error code returned. The interaction between those two isn't specified in the type system.
I personally require discriminated unions (i.e. enums where each variant can hold its own type of data) before I consider a type system adequate.
Then I tried Rust and realized why types were good. (and then Kotlin)
Manually managing memory has turned out to be error prone (for me) and can result in quite nasty to debug bugs (because the type system is lacking), only to mention the most important thing.
I think there is enough out there on C vs Rust, esp. because of the Linux kernel
That said, it was asked about Python (within the context of Rust), and I have limited experience with Pascal and Java, so I will leave it at that.
char *func(char *in, size_t len);
It’s anybody’s guess what the actual input type is, not to mention the ownership or lifetime of the output.It's even better when all those data structures are just dicts, because it's too cumbersome to make serializable classes, so good luck figuring out what fields are in which.
Typed Python 3 is just awkward enough to not make me enjoy it on small scripts and not accurate enough (yet) to be confident in it on large projects.
I don't use numpy & friends so Python doesn't offer me much any more compared to newer typed languages once I go beyond a simple script.
Excited to see where it continues to go and we are writing more scripts in TS at work (without Deno, tho).
Have you done any backend TS? I'm curious what the best practice there is, these days.
I have been on a couple teams that used Node. Everyone was/is still using express and various ORMs / query builders. Knex and Prisma are what I saw.
Lots of neat stuff with Zod and relatedly EdgeDB.
But later I did some backend stuff, I tried to use typed Python and wrote safer code. Then the whole toolchain becomes so cluncky. MyPy, Pylinter, Flake8, etc. They're so slow, and their VSCode extensions are so weird to configure. And I never got the best practice to work with other untyped libraries. It's not that I don't know how to use typed language. Actually I do backend stuff faster in Rust than Python.
It's probably just me or my VSCode acting up.