I'm still amazed at how Python code with type hints looks so different to Python without them. Consider the program near the end of the article - if we remove the type hints:

- The `AstNode` abstract base class becomes completely unnecessary and can be deleted.

- We can't use `@dataclass` and are forced to write an `__init__` method for each class. But this is good, right? "Explicit is better than implicit"?

- It's then clear that our three classes each have two methods, one of which is __init__. Following the advice from "Stop Writing Classes" [0], each class should just be a function (in this case, a generator).

These changes reduce the code above "AST representation of the Ruby statement" from 70 lines to 40, and simplify some of those lines as well - for example, `arg_reprs = (arg.representations() for arg in self.args or [])` becomes simply `arg_reprs = args`.

The knock-on effects of type annotations seem to be doubling the complexity of the code. This is why I'm far from convinced that static type checking in Python is worth the cost.

[0] https://www.youtube.com/watch?v=o9pEzgHorH0

If you're going to use Python with types, then you may as well use Nim. Nim has a similar syntax, but with better performance. You can also interface with Python code via Nimpy.

I love using Nim, it's an absolute pleasure after years of fighting with Python's "I hope I got this right..." style of type guessing. The biggest problem most people run into is that python has a huge library ecosystem.

I haven't actually tried interfacing them, it's next on my docket. Have you found any good learning resources for that?

Nimpy: https://github.com/yglukhov/nimpy

A good test as an example: https://github.com/yglukhov/nimpy/blob/master/tests/tpyfromn...

I've tried it and it works fine. The only problem I ever had was trying to call a specific Python function at the same time from multiple Nim threads. I'm not sure where the fault lay exactly, I just made that part of the code single-threaded and moved on.