There a several special forms for referring to other modules in Elixir. These include:
These each have their own meaning but until you get used to them it can be hard to know which one to use.
To demonstrate each of these special forms let’s create a new test project.
$ mix new use_import_require * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/use_import_require.ex * creating test * creating test/test_helper.exs * creating test/use_import_require_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd use_import_require mix test Run "mix help" for more commands.
This comes with a brand new empty module:
Though the course of this post we’ll fill in and make use of
I’ll start with
alias whose only purpose is to make it easier to type (and maybe read) code. To make an alias we’ll need another module. Let’s add one:
Now, if we want to call this
UseImportRequire we would have to reference with its fully qualified name:
UseImportRequire.AliasMe.function. But that’s a lot to write so we can use
alias like this and use a shorter form of the name:
And that’s it. That’s what
alias does, it let’s you use shorter names. Of course, there is some more to the syntax like aliasing more than one module at a time. And you have some flexibility about how many nested modules you alias away. But in term of purpose, this is it:
alias shortens the name.
There are of course some tradeoffs to consider with
alias. In the simple example above there’s not much to consider. Most likely everything in this app will start with the
UseImportRequire so omitting it has little cost and makes code more readable by eliminating noise. However, as the number of modules in your project grows you may end up with multiple modules with the same leaf module name. Using alias you might end up with code that’s confusing to the reader. You should ask yourself, if I alias am I going to create any ambiguity?
Edit Jan 23, 2016: As Philip Claren pointed out in the comments, it is also possible to pick the name for your alias using the alias-as form. Adding on to our example:
We see we can add an
as: name parameter to
alias to set any aliased name we desire. I’ve picked
AnotherName in the example.
Alias-as can be really helpful if you have two modules with the same leaf-name. For example, if you had
UseImportRequire.SecondKind.AliasMe then simply aliasing both modules wouldn’t work. There would be ambiguity. With alias-as you can choose a different name for one of the modules.
Now, suppose you still think that
AliasMe.function is too much to write. You could use
import. For an import example I’ll create a module to import:
and, I’ll reference this from
So, now we can reference the function as just
function. Nice an short. But at what cost does this come?
It is much easier to create ambiguity in this case. For example, if we imported both the
ImportMe modules we’d end up with two functions named
function and in fact would not compile. Note that the compilation error is lazy in that it would fail unless an ambiguously named function is actually called.
I would recommend using
import sparingly. It removes a lot of information which can be a burden for any reader of your code. However, there are a few cases where import is helpful. If you are writing a module that is very focused in that it makes heavy use of a specific module then
import may make sense. One common example is that in a module that makes extensive use of Ecto queries it is common to import
import macro also allows importing of specific functions or macros. This limits “namespace pollution” and can reduce the chance of ambiguity or confusion. Again, this is common with
Ecto.Query - the documentation recommends:
in order to import only the
require macro instructs the compiler to load the specified module before compiling the containing module. This is only necessary if you want to reference macros from the specified module. For example, we would need:
RequireMe module contained a macro we wanted to use. Nothing special is done to the macro name. We would still need to reference it with its fully qualified name.
In this way
import have some overlapping purpose. Either can be used to access macros in other modules. Though their effects on the namespace differ.
use macro invokes a special macro, called
__using__/1, from the specified module. Here’s an example:
and we add this line to
UseImportRequire.UseMe defines a
use_test/0 function through invocation of the
This is all that
use does. However, it is common for the
__using__ macro to in turn call
import. This in turn will create aliases or imports in the using module. This allows the module being used to define a policy for how its functions and macros should be referenced. This can be quite flexible in that
__using__/1 may set up references to other modules, especially submodules.
The Phoenix framework makes use of
__using__/1 to cut down on the need for repetitive
import calls in user defined modules.
Here’s an nice and short example from the
Ecto.Migration.__using__/1 macro includes an
import call so that if
use Ecto.Migration you also
import Ecto.migration. It also sets up a module property which I assume control Ecto’s behavior.
To recap: the
use macro just invokes the
__using__/1 macro of the specified module. To really understand what that does you need to read the
Now, which of the above macros should you use if you just want to call functions from another module? The answer is: none. Instead, you can just reference the functions directly. Here’s an example module we can reference:
and here is how we can access the function:
That’s it. We don’t need to
require the module. We just use the fully qualified name.
As I’ve mentioned there are tradeoffs for using
import between convenience and clarity. There is another way to help mitigate this tradeoff. The
import macros don’t need to be called at the outer module scope as we have been using them. They can, for example, be called from within another function. Here’s an example using
I’ve had to add this to a new module rather than
UseImportRequire due to a problem with ambiguity.
Using this scoped version we can’t access
function without a qualified name in another function. for example, this won’t compile:
alias macro can be used within a function in the same way.
alias within a narrow scope this way helps limit the amount of “namespace pollution” that happens in your code.
Also, using a narrow scope this way makes the code a little clearer by keeping the
import closer to the reference thereby making it easier for the reader to keep track of what’s going on. This can be especially useful as your modules start to get longer.
Here’s a really nice example of using
use Ecto.Migration call invokes
Ecto.Migration.__using__/1. And we saw above that this macro in turn calls
import Ecto.Migration. The import allows us to write very clean code in the migration. We can call
timestamps without needing to clutter up the code with an
For migrations, this is a good tradeoff a migration is narrowly focused task. When you read these references to
create table, and
add you are in the mindset of thinking about database migrations so this code makes sense.
If you have other tasks that are not as focused you may want to ask yourself if
import is the right choice.