If you're a typing fan as I am, and you are interested in the BEAM then you may want to check out [Gleam, a sibling language of Elixir](https://github.com/gleam-lang/gleam/) which has an Elm/OCaml/F# style static type system. It provides many of the strengths of Elixir but with that statically typed programming style which you may prefer.
(disclosure: I'm the lead dev!)
As another comment has mentioned, what works for me is defining structs for more complex parameters and using them like so:
def some_function(param1, param2, bag_of_options = %BagOfOptions{}) do
With this you'll get autocomplete when you type `bag_of_options.`, a compilation warning* [1] if you try to access properties of `bag_of_options` that don't exist, and it's going to raise an error if you run the code and try to call the function with anything other than a %BagOfOptions{} struct.TypedStruct library is particularly useful when defining structs, allowing you to easily define and document fields, assign them Dyalizer types and set their default values.
This is good enough for me not to significantly hinder my productivity, but YMMV.
[1] Compilation warnings can be treated as errors using --warnings-as-errors, I use it for all production builds
ex: def foo(%Bar{} = bar, how_many) when is_number(how_many)
Also avoiding/limiting variable rebinding should mitigate problems as the language is immutable otherwise.
So IMHO it affects the developer productivity more than create type bugs, because you have to write all that and intellisense kind of sucks (at least in Jetbrains' IDE with the unofficial Elixir plugin).
I also think there is this trap of using unnamed tuples and unnamed maps in the code instead of Structs. I suspect this is fine for a small team and a small code base but if you have a large codebase then it makes it harder to familiarise yourself with the code quickly. Having functions where you can't easily see the types of the inputs and the outputs is not ideal, but once you work out what they are its worse when they don't have names. Of course there are `specs` for this but if you are writing `specs` then what is the point of a dynamic language. I don't really see the advantage of writing specs and not having the compiler do any checking vs writing type definitions and having the compiler check everything is sound.
The learning curve is a bit steep as dialyzer works on erlang terms internally but we've been using it for a rather large codebase for 4+ years now and it does a consistently good job.
The Elixir core team actually mentioned that they want to make improved type support a priority in the last keynotes.
The tldr was that they had patched some of the library code without considering the ramifications of the change. At no point in the talk is it said that “switching to Phoenix was a bad idea”.
Also, none of the issues they talk about are related to the lack of static typing.
This has absolutely nothing to do with your previous post grossly misrepresenting a talk. You hadn’t even mentioned Laravel in it.
Type hinting is not equivalent to a static type system, which is what the parent was asking about.
Finally, in either cases, it changes nothing to the fact that the pain points mentioned in the talk were not caused by the lack of a static type system (which is not to say it cannot cause pain points)
> they thought that switching to Phoenix was a bad idea
If you have the time I recommend re-watching Zack's talk. This is not a take away, or implied.
We also onboard a lot of people with no elixir experience, and most of them are committing code within a day or two. The docs are great and the surface area is pretty small. On boarding just isn't an issue.
IMO it's a ideal balance with pattern matching, it's a very smooth experience all around.
It's best to come into the language with the understanding that this language is different. Copy on write, tail call optimized recursion and pattern matching are wonderful, but they take some getting used to.