Testing all the possible interactions, timings and exceptions that can occur in a parallel programing model where race conditions and deadlocks are possible is exceedingly difficult and in fact impossible.
That said I do don’t put Rust in the same camp as traditional OO languages, it is much closer to what we have in Elixir with modules, functions and structs. Rust also adopted an object ownership model for concurrency as well. Rust has made great strides as a safe systems programming language, unlike traddional OO languages it didn’t adopt inheritance, but does have ability for code reuse with analogues to what we have with Elixir behaviors.
I think Rust actually goes extremely well with the BEAM because it took ownership of state seriously.
Golang is also not a traditional OO language, so it also doesn’t really count. It is also not immune to race conditions despite the ability to enable detection of some race conditions.
Isn’t it interesting that all the real advances are coming from non traditional OO languages?
In respect of options handling in HTTPoison / hackney, that is just sloppy code and a lesson in poor defaults handling. Even with types this would still be a poor state of affairs and unfriendly developer and insecure experience. It is almost as bad as throwing away secure defaults just because the domain didn’t end in “.com” or it just happens to April fools day, really it’s inexcusable, but that’s not the only issues with those libraries.
So with a hispter type system if you don’t specify all options would you expect to get a compiler error?
Would that be a desirable outcome if you don’t want to specify every option always?
I’m playing devils advocate here, be careful what you wish for, the next release of a library may add a new option and now your code breaks. Sure a library may deprecate or remove an option but that is now the responsibility of the library author to detect as they have gone and broken the established interface.
I don’t see how types really help with options as they are mostly optional, aside from perhaps catching a dumb typo. There are options libraries like NimbleOptions that do validation including checks for unknown options being passed, I suggest using them, as we don’t need a type system for that.
I want to be clear that I am not against types, I actually feel that elxir and Erlang go beyond types with pattern matching on both shapes AND values. We have value semantics and we can express our expectations.
I guess I characterise the rest of your response is more about bad practice and not having discipline writing code. No language will protect you from badly structured code, bad idioms and poor structure. Give a simple pen with a single function to a 3 year old and see what creative mess they can make with it.
Refactoring is never a big concern for me, and often a symptom of not having a sound structural methodology to begin with.
I like the OTP book, clean/pure functional cores that are easily tested, separate from the genserver interfaces and boundary modules where failures can happen. I find the CRC (construct, reduce, convert) pattern to result in clear understandable code and the use of context modules tend to provide sufficient insulation from lower level implementation changes.
If a context needs to be refactored even this can be done preserving the existing inteface through delegations until such time you have fully migrated code to a newer interface. It’s pretty easy to find where a module is used or imported so I don’t see a difficulty in determining what needs to be refactored.