Coming from the dynamically typed Ruby, the concept of function overloading was a very foreign concept to me. I hope to succinctly explain the concept to other developers by looking at some code from a project I have been working on.
Elixir has very powerful pattern matching capabilities that allow us to test for specific input cases.
As you can see above, the function make_move is being defined twice here. If you also come from a language like Ruby, you would probably expect this to throw an error. However, because Elixir is able to pattern match in the function’s parameters, Elixir treats these two as completely different functions.
Breaking down the code a bit more, the first clause accepts both a game state and a guess, just like the second one. The first clause MUST match a specific pattern within the game state itself in order to get invoked. We use destructuring in order to bind the value of the game_state to the variable state. We then use a guard clause to test for certain values of that state. Only won and lost games are passed through the first function clause. The second clause handles all other calls to this function. As we can see, the second function makes an additional call to another function: accept_move. We would no longer want to be accepting moves on a won or lost game so this functionality makes sense.
The power here is amazing! Since Elixir does struggle a bit with looping patterns and conditional statements, function overloading is great way to subsidize those! Getting in the habit of writing overloaded functions instead of if else statements has helped my shift my mindset even more towards the functional paradigm. I challenge you to not write a single if else statement in your next Elixir project!