Let’s demystify the frequently used
use keyword in Elixir.
We can use the
use keyword in Elixir to package behaviour and implementation for use in other modules, similar to abstract base classes or mixins in other languages. Mixins reduce the amount of boilerplate in your modules, especially for library authors.
Mixins in Elixir are similar on the surface to
Concerns in Ruby and
Traits in Scala. Before we start, you should be somewhat familiar with behaviours and macros.
use requires the given module and calls the
__using__ macro defined for that module. To illustrate, both examples below are equivalent:
Now take a look at the
Filter module defined below:
First, we define a behaviour callback
transform with the
@callback attribute. Modules that
Filter module will need to implement
We then define the
__using__ macro using
__using__ is called whenever the
use keyword is called with this module.
We can accept any
_params specified in the
use call, but we’re not using any for this example.
Within the body of the
__using__ macro, we use
quote to return a quoted expression. The execution of this quoted expression is deferred until runtime, when the
use keyword is executed within the user module. This lets us inject code to user modules.
Read this if you need a refresher on macros.
Here we use the
@behaviour attribute to denote that modules that use
Filter must implement the callbacks defined in
We define some default concrete implementations of
greet/1 which will be injected to user modules. We then use
defoverridable to denote that it can be overriden.
Now let’s take a look at a
MyFilter module which
In our module we override the
greet/1 function with our own implementation.
If we didn’t explicitly add
defoverridable, new definitions of
greet/1will not be picked up and we would use the original definition instead.
However, compiling the above module gives us the following warning:
warning: undefined behaviour function transform/1 (for behaviour Filter). This is because we’re missing a required behaviour implementation
transform. Let’s implement all our behaviour callbacks:
MyFilter will now compile without warnings. With just a single line of code, we’ve injected
MyFilter with behaviours and default overridable implementation:
If you’ve played around with the OTP, you would have come across
use GenServer. The
GenServer module uses
use to give developers a set of default client-server functionality which can be overriden. Here is an excerpt of its
If you don’t need metaprogramming, control over method overriding, nor pass in parameters to your
use call - look into using
Mixins are a powerful way to organize your modules that is well worth its place in your Elixir toolbox. I hope I have helped you dispel the magic of
use. You should have a better understanding on how you can use mixins to inject code into other modules and reduce boilerplate.