There a several special forms for referring to other modules in Elixir. These include:
useimportrequirealias
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
endThough 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
endNow, 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
endAnd 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
endWe 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
endand, 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
endSo, 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
endif 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
endand we add this line to UseImportRequire:
use UseImportRequire.UseMeUsing 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
endThe 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
endand here is how we can access the function:
def reference_test do
UseImportRequire.ReferenceMe.function
endThat’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
endI’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
endThe 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
endThe 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.