I worked on a project that did this. Drove me absolutely nuts. It's like having all the worst parts of a dynamic language and a static language with none of the benefits.

I'd much rather just work in a statically typed language from the start.

I too would much rather work in a statically typed language, but sometimes you have to work with what you’ve got.

These systems are part of the core banking platform for a bank so I’d rather some initial developer friction over runtime incidents.

And I say initial friction because although developers are sometimes resistant to it initially, I’ve yet to meet one who doesn’t come to appreciate the benefits over the course of working on our system.

Different projects have different requirements, so YMMV but for the ones I’m working on type hints are an essential part of ensuring system reliability.

I'm not opposed to type hints, I use them everywhere. It's specially the strict linting.

But it's a fair point. If you truly have no option it's better then absolutely nothing. I really wish people would stop writing mission critical production code in Python.

I feel like it's more often a result of suffering from success that leads to these situations, rather than a lack of foresight to begin with.

For example I work on a python codebase shared by 300+ engineers for a popular unicorn. Typing is an extremely important part of enforcing our contracts between teams within the same repository. For better or for worse, python will likely remain the primary language of the company stack.

Should the founder have chosen a better language during their pre-revenue days? Maybe, but at the same time I think the founder chose wisely -- they just needed something that was _quick_ (Django) and capable of slapping features / ecosystem packages on top of to get the job done.

For every successful company built on a shaky dynamic language, there's probably x10 more companies that failed on top of a perfect and scalable stack using static languages.

A statically typed language doesnt prevent developers from using the equivalent of dict[str, Any].

Well, some do. Let's not pretend all static type systems are the same.

Type hints seem fantastic for when you're in maintenance mode and want to add sanity back to a system via automated tooling.

However for new projects I find that I'd much rather pick technologies that start me off with a sanity floor which is higher than Python's sanity ceiling. At this point I don't want to touch a dynamically typed language ever again.

Always fun to inherit a data-scientist derrived chunk of pytjon code for which every type hint is 'takes a dataframe' and 'returns a dataframe'..

What exactly drove you nuts? The python ecosystem is very broad and useful, so it might be suitable for the application (if not, reasonable that you'd be frustrated). With strict mypy/pyright settings and an internal type-everything culture, Python feels statically typed IME.

It's not even close compared to working with Java or Go or any language built with static typing in mind.

To be clear, I'm not opposed to type hints. I use them everywhere, especially in function signatures. But the primary advantage to Python is speed (or at least perceived speed but that's a separate conversation). It is so popular specifically because you don't have to worry about type checking and can just move. Which is one of the many reasons it's great for prototypes and fucking terrible in production. You turn on strict type checking in a linter and all that goes away.

Worse, Python was not built with this workflow in mind. So with strict typing on, when types start to get complicated, you have to jump through all kinds of weird hoops to make the checker happy. When I'm writing code just to make a linter shut up something is seriously wrong.

Trying to ad typing to a dynamic language in my opinion is almost always a bad idea. Either do what Typescript did and write a language that compiles down to the dynamic one, or just leave it dynamic.

And if you want types just use a typed language. In a production setting, working with multiple developers, I would take literally almost any statically typed language over Python.

But TypeScript erases (its) types at runtime, exactly like Python. Python is Python's TypeScript. Whether you want TS or JS-like semantics is entirely dependent on whether you use a type checker and whether you consider its errors a build breaker.

I'm not sure what you're trying to say here. If you mean Python's type annotations are erased at runtime... Okay? It still has runtime type information. It's not "erasure" as that term applies to Java for example. And Typescript compiles down to JavaScript, so obviously it's runtime behavior is going to be the same as JavaScript.

In my view it's always a mistake to try and tac static typing on top of a dynamic one. I think TS's approach is better than Python's, but still not nearly as good as just using a statically typed language.

The fact that the types are reflected at runtime is what makes FastAPI/Pydantic possible, letting us use Python types to define data models used for serialization, validation, and generating. In TypeScript, we have to use something like Zod, instead of "normal" TypeScript types, because the types are not reflected at runtime.

I think a couple of things have to be untangled here.

The problem we are talking about in both Python and TS comes from the fact that they are (or compile down to) dynamic languages. These aren't issues in statically typed languages... because the code just won't compile it it's wrong and you don't have to worry about getting data from an untyped library.

I don't know a lot about Zod, but I believe the problem you are referring to is more about JavaScript then TS. JavaScript does a LOT of funky stuff at runtime, Python thank God actually enforces some sane type rules at runtime.

My point was not about how these two function at runtime. My point was that if you want to tac static typing onto a dynamic language, Typescripts approach is the better one, but even if can't fix the underlying issues with JS.

You could take a similar approach in Python. We could make a language called Tython, that is statically typed and then compiles down to Python. You eliminate an entire class of bugs at compile time, get a far more reliable experience then the current weirdness with gradual typing and linters, and you still get Pythons runtime type information to deal with things like interopt with existing Python code.

Typescript requires a compiler to produce valid Javascript. Python 3 shoved types into Python 3 without breaking backwards compatibility I think.

You would never have typing.TYPE_CHECKING to check if type checking is being done in TypeScript, for example, because type hints can't break Javascript code, something that can happen in Python when you have cyclic imports just to add types.

Not it doesn’t. It doesn’t throw errors, but they’re still introspectable in python, unlike typescript

I would say mypy is better than nothing but it still misses things sometimes, and makes some signatures difficult or impossible to write. I use it anyway, but patched-on static typing (Erlang, Clojure, and Racket also have it) seems like a compromise from the get-go. I'd rather have the type system designed into the language.

Mypy is trash but Pyright is very good.

I went from mypy to pyright to basedpyright and just started checking out pyrefly (the OP), and it's very promising. It's written in Rust so it's very efficient.

You know you can just use a compiled language with statically checked types, right?

For the kind of work I'm using Python for (computer vision, ML), not really. The ecosystem isn't there and even when it's possible it would be much less productive for very little gain. Typed Python actually works quite well in my experience. We do use C++ for some hand-written things that need to be fast or use libraries like CGAL, but it has a lot of disadvantages like the lack of a REPL, slow compile times and bad error messages.

Python is the second most popular programming language in the world. It's not that easy to avoid.