The Elixir ecosystem is top notch. Phoenix, Ecto, Livebook, Mix, Hex, Nx, Axon, Scholar, Explorer, Broadway, and Rustler are libraries and tools that make it hard to use anything else even when the language doesn't have types. I never enjoyed programming in a language more than Elixir.
Tho, they are currently are in the process of researching a type system for Elixir.
Especially, if I'm trying to bring and old(er) codebase (Elixir 1.9) up to the newest release... and upgrading packages, checking if things are likely to brake etc.
I constantly find myself realising that I cannot trust the compiler, which means more defensive coding, more tests, more time spent...
As an example, upgraded postgrex. The return tuple of one its functions had changed from 2 elements to 3 elements. Did the compiler caught it? No. Had I had dialyzer type spec-s on my private function, I would at least gotten a warning. Thus, morale of the story: always type spec even your private functions , especially if they use 3rd party code.
In dynamic languages the typing is just moved to the test suite. We can see that with the way type hints are enforced in Python and Ruby - by running a program that takes those hints and essentially runs a bunch of tests.
If you look at the code of Terraform, for example, you'll see that it spends a lot of time trying to get around the limitations of Go typing.
The rise of dynamic languages in the early 2000s was a direct result of the perceived limitations of the type systems of Java, C++, etc. and the ability to use a different way of constraining code (Test driven development, behaviour driven development).
Now it is swinging back the other way with the implementation of more flexible type systems.
Before too long the limitations of those type systems will start to grate and the pendulum will swing back to dynamic languages again - based upon some new innovations there.
Get used to the back and forth in IT. The static/dynamic cycle is but one of many. (Centralisation/decentralisation is the other main one). You'll find lots of work in IT is reimplementing existing stuff in the current fashionable paradigm.
Then again perhaps LLM programming will become the new thing. ;)
E.g. I wrote c# and Python for a living. Especially in the mid 2010s python was much better for the Microservices I wrote because they are so small that you could unit test them exhaustively. The lack of ceremony and small domain model made development super easy. C# just felt clunky and limiting. You spend lots of time wrangling types which could have been figured out by the compiler.
Nowadays c# has gotten a lot of features that reduce type ceremony and extend their usefulness (nuallibility checks, better tuples etc) so it feels much more ergonomic (unlike java where the culture still goes off on needless verbosity) . Python also got types which I use a lot, it helps to document it for other developers, ides and spot bugs.
FWIW, there are definitely ways to do it "right" in languages like Python, but it's a slightly different approach to dev than in other languages, so people create a mess if they don't shift, or if they are just untrained/sloppy/lazy/whatever. But when you have a team of people doing it right, it's really quite nice, even as the codebase grows large.
But if even just one person on the team can't be trusted to do it that way, you're probably better off in some other language.
Perhaps they are not a big deal, but if they are too restrictive, then maybe the next best thing would be to have anything go (like in Python or Javascript), but the compiler guides you to where it's having trouble inferring things.
Then you can appreciate which tool to use and when, rather than getting distracted by strange religious notions about how "this language is better than that one". It also prevents you from forcing conventions the language doesn't align well with, and that if you're going to _try_ to do so - you should do so gradually and very carefully.
The thing that drives me crazy about Python typing is that I need to import stuff to make full use of the type system. Like I get that the typing is option because that would be a huge language overhaul, but it feels weird that so many tools get hidden inside libraries (standing or otherwise).
foo: list[str] = ["a"]
bar: int | None = 1Other aspects of a language can matter more than the types. Python, for example, was a well constructed language in many ways that make up for much of its disadvantage on the typing side.
Many commonly used statically typed languages have poor type systems. They will both fail to detect some important errors (like null pointers) and get in the way when writing more generic code.
Whether you specify them or not, the data moving around your application _has_ types.
Strings, ints, booleans - they're all there. You're just keeping that information in your head while you work.
By writing them in the code, you're simply offloading all of that and saying "I trust the computer to validate this better than I can." Which is exactly the kind of thing we should be trusting computers to do. They won't forget what type something is 5 function calls deep and they won't get tired from lack of sleep.
I know I prefer having the computer tell me I made a mistake while I'm developing something rather than a user emailing me after I've shipped it.