There a several special forms for referring to other modules in Elixir. These include:
use
import
require
alias
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:
defmodule UseImportRequire do
end
Though the course of this post we’ll fill in and make use of use
, import
, require
, and alias
.
Elixir alias
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:
# lib/use_import_require/alias_me.ex
defmodule UseImportRequire.AliasMe do
def function do
IO.puts "#{__MODULE__}.function"
end
end
Now, if we want to call this function
from 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:
defmodule UseImportRequire do
alias UseImportRequire.AliasMe
def alias_test do
AliasMe.function
end
end
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:
defmodule UseImportRequire do
alias UseImportRequire.AliasMe
alias UseImportRequire.AliasMe, as: AnotherName
def alias_test do
AliasMe.function
end
def alias_as_test do
AnotherName.function
end
end
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.FirstKind.AliasMe
and 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.
Elixir import
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:
# lib/use_import_require/import_me.ex
defmodule UseImportRequire.ImportMe do
def function do
IO.puts "#{__MODULE__}.function"
end
end
and, I’ll reference this from UseImportRequire
:
defmodule UseImportRequire do
alias UseImportRequire.AliasMe
import UseImportRequire.ImportMe
def alias_test do
AliasMe.function
end
def import_test do
function
end
end
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 AliasMe
and 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 Ecto.Query
.
The 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:
import Ecto.Query, only: [from: 2]
in order to import only the Ecto.Query.from/2
macro.
Elixir require
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:
defmodule UseImportRequire do
require UseImportRequire.RequireMe
end
if the 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 require
and import
have some overlapping purpose. Either can be used to access macros in other modules. Though their effects on the namespace differ.
Elixir use
The use
macro invokes a special macro, called __using__/1
, from the specified module. Here’s an example:
# lib/use_import_require/use_me.ex
defmodule UseImportRequire.UseMe do
defmacro __using__(_) do
quote do
def use_test do
IO.puts "use_test"
end
end
end
end
and we add this line to UseImportRequire
:
use UseImportRequire.UseMe
Using UseImportRequire.UseMe
defines a use_test/0
function through invocation of the __using__/1
macro.
This is all that use
does. However, it is common for the __using__
macro to in turn call alias
, require
, or 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 use
and __using__/1
to cut down on the need for repetitive alias
and import
calls in user defined modules.
Here’s an nice and short example from the Ecto.Migration
module:
defmacro __using__(_) do
quote location: :keep do
import Ecto.Migration
@disable_ddl_transaction false
@before_compile Ecto.Migration
end
end
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 __using__/1
macro.
Referencing Modules
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:
# lib/use_import_require/reference_me.ex
defmodule UseImportRequire.ReferenceMe do
def function do
IO.puts "#{__MODULE__}.function"
end
end
and here is how we can access the function:
def reference_test do
UseImportRequire.ReferenceMe.function
end
That’s it. We don’t need to use
, import
, or require
the module. We just use the fully qualified name.
Scope
As I’ve mentioned there are tradeoffs for using alias
and import
between convenience and clarity. There is another way to help mitigate this tradeoff. The alias
and 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 import
:
defmodule UseImportRequire.WithScope do
def scope_test do
import UseImportRequire.ReferenceMe
function
end
end
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:
defmodule UseImportRequire.WithScope do
def scope_test do
import UseImportRequire.ReferenceMe
function
end
def failing_scope_test do
function
end
end
The alias
macro can be used within a function in the same way.
Calling import
and 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 alias
or 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.
An Example
Here’s a really nice example of using import
:
defmodule Orthrus.Repo.Migrations.CreateUser do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string
add :username, :string
add :password_hash, :string
add :email, :string
timestamps
end
end
end
The 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 create
, add
, timestamps
without needing to clutter up the code with an Ecto.Migration
prefix.
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.