Software Engineer

Elixir tricks: Delegating a function to another module using defdelegate

Sometimes, we need to write functions in Elixir that serve as a bridge between modules, allowing us to use specific functionality from another module, see the following example:

def categories, do: Category.categories

The only responsibility of this function is to call the categories function from the Category module. Generally, programmers do that to “hide” unnecessary context in the code. If we are talking about Product, is explicitly what a Category is, so isn’t necessary to explicit show that we are taking it from the Category module.

Elixir provides us a macro to hide this context, where you can delegate functions to another module: defdelegate. It comes from the Kernel module, so isn’t necessary to import it. It’s a powerful and convenient tool in Elixir that helps us streamline our code and promote code reusability.

With defdelegate/2, we can easily forward function calls to another module, effectively passing the responsibility of handling the function’s logic to that module. It simplifies our codebase and makes it easier to maintain, as changes to the delegated functions can be made in one place rather than scattered across multiple modules.

The syntax for using defdelegate is straightforward. Within a module, we use the defdelegate macro followed by the name of the function we want to delegate and specify the target module to which the function call should be forwarded. The target module must already have the delegated function implemented, ensuring that the delegation is valid. An example of using defdelegate would be as follows:

defmodule Product do
  defdelegate categories, to: Category

defmodule Category do
  def categories, do: ["Apparel and Fashion", "Electronics", "Home and Kitchen"]

In this example, the Product module delegates the function categories/0 to the Category module. Now, if we call Product.categories/0, it will be forwarded to Category.categories/0:

> ["Apparel and Fashion", "Electronics", "Home and Kitchen"]

It’s important to note that defdelegate/2 only forwards the function call. It does not automatically generate the function’s documentation or type specifications. To maintain clear documentation and type specifications, we need to do it explicitly for the delegated function in the delegating module.