LSPs are a sort of middle ground where I derive a lot of the value from, and even weak type systems really help the analysis of the LSP. They make me immensely more productive. I've been writing a lot of TypeScript lately, and I may not have taken the plunge to the frontend if LSPs hadn't made it dramatically easier. So in that case the impact on my productivity is +100%.
Especially when dealing with someone else's code, it can be a ceremonial roadblock for moving data around that could easily be processed without issue but artificial constraints in order to service an orthodoxy have been erected to make things needlessly more brittle.
In the name of reducing bugs, it in practice can often impose workarounds that increase the chance the confusion and more bugs.
This isn't always the case but most of the time things are built in incompetent hands (including my own). Accommodating for this reality is probably the better move. Imposition of absolutist rules doesn't improve the behavior of incompetency, it just makes it more subversive and the defects it creates more insidious
I try not to think of it as whining, I try to think of it as the type checker helpfully pointing out, "hey, you haven't done your due diligence here." I ran into this advice when learning Rust and it made it much more pleasant. In the same way that you might chase a series of failing tests to finish a task, you can chase a series of type warnings. I have them integrated into my editor, so usually the "series" is length 1 because I'm knocking them out as they arise.
The projects I've worked with that were previously untyped (Python SaaS backends), I never actually ran the type checker on the entire source code. But if I had a megabyte of type errors that weren't really going to provide value to the business, yeah that would feel like a waste of time. Unless we were having serious quality issues and it was between annotating the entire codebase and rewriting it from scratch, I'd leave well enough alone - it's a tool, not a religion. But the new code I'm checking in, that's getting annotated for sure.
I don't really see types as arbitrary absolutist rules. They're the semantics of the language, they're the rules you're following whether you have a typechecker or not. And if you violate them you're gunnuh get bugs and crashes regardless. I too am a flawed human who frequently makes mistakes, and I like having them caught immediately, before I've even checked my code in.
It can be both true that you haven't done your due diligence in the sense that you haven't taken care to handle the entire range of all possible inputs (or at least it seems like it when you're only considering the relevant code section in isolation), but that as a practical matter it's just not a problem. So the time spent attending to what it's nagging you to take care of is time wasted. Alternatively, you're not even a program maintainer, you're just trying to build the thing so you can run it for your own one-off use, but it refuses to even build because of some issue totally irrelevant to what it is you're about to use it for.
The approach that the TypeScript compiler takes is beneficial—complain about the thing, but don't let that prevent the compiler from running to completion and still producing a program that you can run.
There's type narrowing, warnings for not awaiting Awaitables, generics, protocols/interfaces, even whole function signatures can be lifted into the type system (ParamSpec). Dictionaries can be turned into typed versions as well, which is quite powerful.
By default, it's weaker, as it's optional. Rust's isn't optional. But start a greenfield project with type annotations and you won't be disappointed, I hope. Features like ADTs will still be absent, but I feel that's an orthogonal issue to type safety etc.
"Python wasn't weak, I was" is an accurate call out, I have abandoned complex annotations in Python before because there was a bunch of cruft involved and the documentation wasn't always as straightforward as I'd have liked.
The lack of ADTs (and thus a Result type) combined with the lack of exceptions as part of the function signature (like Java) make it really difficult to develop a reliable application. Maybe that's not quite a type safety issue, but it's a safety issue for sure.
Digging in, it looks like people are working around this by returning exceptions, which is very clever and I hadn't thought of it before.
Never came across that! Where did you find that? Pretty interesting. I'd call it deeply unpythonic and very surprising. It's unfortunate to not have a Result type/idiom, but IMHO faking it isn't the way to go either.
https://stackoverflow.com/a/74192977
I haven't given it a great deal if thought, it would probably mess up stack traces if you raised it later, which would be bad. I agree it's unpythonic, I've implemented result-like types in Python before which has worked okay.
ETA: That answer links to this library, which uses a decorator technique that I think will still print a good traceback if you reraise it later (definitely will if you use `raise ... from` syntax), but I can't test it at the moment:
https://github.com/alexandermalyga/poltergeist
I probably won't be putting this into production anytime soon but it was charming food for thought.
you either create a complex program, with simple language constructs, or a simple program with complex language constructs
if you use only the simplest types, your program will be very complex, if you put all the complexity in types, your type system will be very complex
you need to balance things
i think OOP is the extreme where all the complexity is in the Object system (or types system)
functional programming with complex types, i think give a nicer balance, where some logic goes into the program flow and some goes into types
anyway balance is everything, keep things balanced , dont lean too much into any direction
Languages and libraries that are well designed and compose better end up being great at taming complexity. Functional languages are made to compose better, but their popularity is a hint that they are not that simple to use effectively.
do you have any in mind ?
I often think about a blend of functional and OO as 'protocol graphs' but it's a faint idea for now
The thing is that while array programming is amazing for some specific problems it's not going to help you make sure that you don't have memory errors, race conditions, or wrong states in your program
> it's not going to help you make sure that you don't have memory errors, race conditions
Not only does array programming abstract away those kinds of errors, it also solves the problems that I care about: code that is faster, more expressive, and easier to read and write. Unwieldy type systems do not help me solve those problems.
Type systems also enable optimizations that do make code faster. Eg monomorphization. Consider that JITs makes untyped code faster by inferring types at runtime. See also this comment https://news.ycombinator.com/item?id=37376010
I honestly can't make heads or tails of the idea that type systems don't help make code more expressive? They allow you to express your precise intent in a way that's legible to both humans and the compiler.
You needn't use your real name, of course, but for HN to be a community, users need some identity for other users to relate to. Otherwise we may as well have no usernames and no community, and that would be a different kind of forum. https://hn.algolia.com/?sort=byDate&dateRange=all&type=comme...
Also, can you please stop using trollish usernames? Those aren't allowed either, because they basically troll every thread the account posts to. https://hn.algolia.com/?sort=byDate&dateRange=all&type=comme...
Was it ever not?
As a library developer I spend substantially more time on making the types right than the actual implementation. It can be brutal at times.
I guess you can complain that Typescript forces you to spend time making sure your code meets a minimal bar of correctness...