Sharing Base Functionality in Elixir
At Rekki we write a lot of Elixir code. Elixir is a language whose influences are a mix between object-oriented programming and functional programming. This is evident in how it provides tools typically of an object-oriented style in a functional way.
One such implementation of these tools is for polymorphism, which is the idea that something (function / module) can have different forms, based on the type of input. These tools are Behaviours and Protocols.
You would use Protocol when the desired behaviour of the functions are the same, but the data type is different. Here is an example: a Protocol for a
size function, the desire of all the functions is the calculate the length of the input, but the data type is different, here is the example from the Elixir website:
Behaviours are used when the desire of the modules are the same, but the situation is different. An example being parser modules, the desire of each module is to parse data of a specific format, the situation of what that format is changes, here is another example from the Elixir website:
Now that we’ve seen how you can achieve polymorphism in Elixir, how would we allow each implementation to share functionality?
By making use of a mix of either Behaviour or Protocol along with the
use macro, then polymorphism and extension can be achieved at the same time.
An example would be a
FilePrinter Protocol, it would parse a file in a particular format and then print that to standard output. The parsing of the file would change based on the file type, but the way it would print would remain the same. Therefore, we would only want to write the implementation of how its prints to standard output once, and this would be shared between all implementations:
And an example of the Behaviour being implemented:
We would then be able to call JSONPrinter in the following manner:
It might seem strange at first, using both
@behaviour on the module. It also looks like we are introducing coupling where it is not needed, coupling the implementation of the parser to the implementation of how it is output. What if we wanted to extend the functionality of the parser, maybe to parse JSON as it comes in from a web request? This is true, coupling has been introduced where it doesn’t need to be, however this coupling has allowed the possiblity of some benefits: duplicated code is kept to a minimum, being able to add new scenarios does not require much code and the user of this code is presented with a clean interface to work with.
In the blog post you have seen how to share functionality between different implementations of a Protocol or Behaviour with the use of the
use macro. It may not fit every situation, so take a critical eye to it before deciding to implement it yourself, but under the right circumstances it is a great asset to have when implementing polymorphism in Elixir.
Want to know more us? Go here.