IME this kind of "magic" very quickly loses its appeal when you have to debug it. The author's idea of libraries wrapping this stuff up so users don't have to care about it just doesn't pan out at all in my experience.
IME this kind of "magic" very quickly loses its appeal when you have to debug it. The author's idea of libraries wrapping this stuff up so users don't have to care about it just doesn't pan out at all in my experience.
This is cool if it works. Good luck when it doesn't due to user error, and more luck if it doesn't due to a bug.
You need to develop a whole strategy to tell the user what went wrong and how, and why, basically from scratch; admitting that you didn't, in fact, execute the generator, and that they have to change it. because of that.
It's pretty awesome that it's possible. And it would be pretty amazing to write something like that. But... idk if it's a good idea from a developer stand point. I like being able to understand what's happening in a library with a single click.
var result = await (from c in Customer where c.orders.sum(o => o.price) > 1000 select c).ToListAsync();Fine! These things are based on metaprogramming; chances are you've used some of them.
* In nearly any language, nearly any ORM, for describing tables / models.
* In Rust, serde, and nearly everything you #[derive].
* In Java, Lombok, Hibernate, and most dependency-injection tools.
* In Java, most mocking / stubbing libraries.
* In Python, tons of stuff, from standard library (dataclasses, namedtuples) to various parts of Django, etc.
Judging by the sustained and widespread use of these things, they are not exactly complete failures.
Similarly, any heavy use of lambdas in java will make step debugging confusing. As will any annotations you may use. Which is largely why many people grow to hate annotations.
Which is all a fancy way of saying we learn the same lessons again and again. Used smartly, all of these are great tools. Defining what is "smartly" is a place of dragons.
On the contrary, static typing in Python is still extremely nice to have. When I was at the peak of my Python career, I had a bug where I changed the return type of a function to be a tuple, and somehow unit tests missed one of the worst possible invocations, leading to an awful failure that occurred after a payment was processed but before actually completing the task, causing it to be retried repeatedly. To be clear, like any failure of this nature, it is one caused by many different problems, and we employed many different solutions; we started paying attention to test coverage, we began using MyPy (it was still quite new; this was also in Python 2 so it needed type erasure compilation among other things) and we made our payment processing logic more robust to prevent processing the same payment twice even in the case of everything else (like retry logic) failing to stop it again. But the thing that sucks is, fairly simple type inference without any additional type annotations could've detected that sort of bug without a potential for false positives.
So I feel like it's silly the way that some dynamic language proponents feel like static typing systems on top of dynamic languages is all from outsiders. On the contrary, the relative weakness of MyPy is actually what ultimately made me quit using Python, and if I had to use it today, then yes, of course I would opt for the highest degree of type safety, as a primary concern above being "pythonic." I like pretty code, but I like correct code more.
Sometimes this does prevent "better" solutions from working, but in my opinion nearly 100% of the time that this is the case, it's because:
- The type system in question is not sufficiently advanced to express the types elegantly.
OR
- The approach is inherently not safe and probably not a good idea. Like patching the request object inside of middleware in Django, for example.
I think TypeScript proves that with a sufficiently advanced type system, even arguably bad ideas can be type safe. For example, the ability to express dotted object paths safely in modern TypeScript is pretty impressive, and lets you map out older JS that does this accurately, but in general that seems like an unnecessary trick that will just make your code slower at runtime for the slightest terseness improvement over alternatives, lacking a language with sufficiently advanced metaprogramming.
The thing is though, eventually this thought process comes true. If a given programming language community winds up bleeding members who are moving on due to the lack of better static type checking systems, then the people left will invariably be much more likely to be against static type systems.
You could have introduced an UAT environment and done basic QA.
Type checking is not a replacement for QA testing. However, it is much cheaper and also finds bugs that might be incredibly hard to find through random chance or through structured smoke testing, because it does not depend on how common a given code path is. QA testing is even less likely to find issues than automated test suites, since at least you can qualify branch coverage with test suites (which still will not enumerate all of the possible code paths, and thus... Type checking is incredibly useful.)
So while type checking really is not a replacement for QA, QA is also not a replacement for type checking.
I ultimately quit Python for many reasons, but the desire to write more correct code was the big one. Not just me, but my entire team was burned out on Python. Investing so much into unit testing and increasing coverage, upgrading Python versions, and adding type checking as much as possible was still not yielding the benefits we'd hoped for despite how much effort it took. We eventually found much better success with the then-still-new Go programming language, which we used on most new projects going forward. There was still the occasional head scratcher in production, but deployments were as quiet as a church mouse. YMMV of course; there's plenty of success and failure stories with any programming language. I think we were all ready and willing to put a lot of investment into improving our robustness situation and just felt that we got more out of that effort with Go than Python for our programs. Python still has plenty of advantages too, so we didn't completely quit it; we wound up using Django and SQLalchemy here and there, but definitely most stuff moved to Go over time. (And personally, I stopped using Python for those things too, since moving on.)
It's a pity. I really like types but mypy is just not great. It's ok, buts that's not enough for python.
> If a given programming language community winds up bleeding members who are moving on due to the lack of better static type checking systems, then the people left will invariably be much more likely to be against static type systems.
This reminds me of one of the things I hate most about the Python library ecosystem: libraries attempt to camouflage type errors resulting from changes like this. Something that should be an immediate runtime error turns into a subtle production bug. I discovered an error of this kind just this week while doing what I often have to do when refactoring someone else's code in Python: reading and distrusting every single damned line.
Which led to reading the source code of a third-party library, where I discovered something that I've seen often enough that I'm starting to think it's some kind of norm in Python libraries:
A function is documented to take a list of Foo. But if the value you pass is not a list, it doesn't throw an error. If the value is None, it uses []. If the value is a set or a generator or a tuple, it uses list(value). If the value is none of those things, it uses [value]. Now that it has a list, it looks at the values in the list, and for any value that isn't a Foo instance, it does its best to handle it. For example, if it's a str, it passes the value to the Foo constructor.
This might feel helpful for beginners and non-professionals just trying to get something to work on a single data set, testing everything by hand, but it amounts to passive-aggressive betrayal when you're trying to maintain a large codebase with multiple contributors. In the case I discovered this week, instead of an immediate runtime error prompting the programmer to make a trivial fix in their code, we had a feature subtly degraded in production for months. Very "helpful."
And yes generally agree that Paramus should be not be too accepting. It gets confusing fast.
Python has an Iterable type class for this.
https://docs.python.org/3/library/typing.html#nominal-vs-str...
https://docs.python.org/3/library/collections.abc.html#colle...