Coming from a decade of work in JavaScript, having these four different directives in Elixir felt strange at first. Wait, what? use
is not just a directive, but actually a macro? Ok, things are really confusing.
Let’s try to clarify them.
Alias
alias
allows us to use a shorter name for a module, making it easier to reference in the code. So, when a module has a long (nested) name, we can omit the full name by using an alias. Instead, we only need to type the last part of the module's name.
We can also specify a custom alias
using the :as
option. This can be useful when we have multiple modules with the same name or when we want to give the alias a more descriptive name.
Pretty straightforward. With alias
we can remove the extra baggage of long module names and make our code more readable. More about aliasing in Elixir can be found here.
Import
import
allows us to bring functions or macros (more on macros later) from another module into the current scope without prefixing them with the module name.
Let’s see how we can use import
in the same example.
There’s a downside to using import
, though. Sometimes it’s not immediately clear where a function is coming from. So if you’re using import
, opt for only
or except
to avoid naming conflicts. In this example everything that AdminNotifications
has, is now available in the current scope, which can be a bit dangerous.
I try to avoid import
but make an exception for tests. For example, in my tests, I don’t mind importing all the fixtures I wrote, but in the actual code, I prefer explicitness, even if things get a bit lengthy.
Read more about import
here.
Require
Macros are a metaprogramming primitive; code that generates code. They are executed at compile time, contrary to functions that get called at runtime.
So, if we want to use a module with macros, we need to require
it first so it will be available at compile time.
Let’s create a simple module with a macro :
And let’s put it to use in our worker module.
Everything works, but it looks pretty busy.
If we want, we can replace require
with import
. By doing this import
pulls everything from the module into the current scope, and implicitly runs require
for us.
This isn’t true for alias
as that only creates a shorter alias for the module, and we still need to require
it ourselves.
More about require
can be found here.
Use
Speaking of macros, use
is a macro itself. It invokes the __using__/1
macro of another module. This is often used to set up behaviors or inject code into the current module.
That's a bit abstract, so let’s revisit our previous example.
Now, we can use this module in our worker.
By doing this, we eliminated the need to call MyApp.Helpers.LogThisPlease.info
and can now call info
directly. It is a typical pattern in Elixir, especially in libraries. For example, Oban uses use Oban.Worker
to set up all the necessary plumbing to make this module work as an Oban job.
Of course, it poses the same problem as import
- it’s not immediately clear where the function comes from.
TL;DR
- Alias provides shorter names for modules, making them easier to reference.
- Import brings specific or all functions and macros into the current scope.
- Require ensures a module is loaded and is necessary for using macros.
- Use invokes the
__using__/1
macro of another module, often used for setting up behaviors or injecting code.