elixir use vs import require vs import elixir import vs use import vs require module-alias import functions writing code integer module difference between import and require

Alias, import, require and use in Elixir - complete guide with use cases.

In most programming languages we often deal with instructions responsible for handling dependencies. Elixir is no different.

In Elixir, dependency is nothing more than compiled module which for some reason you want to use in another module. There are a couple of instructions that we use in Elixir to either make it easier or possible to interact with modules.

In this blog post I'll explain and present use case examples of four of them:

  • alias,
  • require,
  • import,
  • use.
Table of contents

    Elixir alias

    In Elixir, the module names might be quite long. As a result, it's very time-consuming to use them a lot across the entire app. Such code is also rather difficult to read.

    Let's take a look at this example:

    defmodule Curiosum.Authentication.Admin do
      defstruct [:id, :email]
    
      def changeset(admin, params \\ %{}) do
        # implementation is not important here
      end
    end

    Now, imagine you want to call the changeset/2 function with the Curiosum.Authentication.Admin struct as its argument. You have to use the whole module name to do that:

    Curiosum.Authentication.Admin.changeset(%Curiosum.Authentication.Admin{})

    The above example doesn't look good. Fortunately, we have an alias/2 macro. Its purpose is to give the module an alternative name:

    alias Curiosum.Authentication.Admin, as: AdminResource
    
    AdminResource.changeset(%AdminResource{})

    That's way better. With :as option, you specify the name of the alias. However, it's optional. Calling alias without :as by default uses the last part of the module name, which in this case is Admin:

    alias Curiosum.Authentication.Admin
    
    Admin.changeset(%Admin{})

    It's very common to skip :as option in the Elixir world. After all, calling alias in the following way:

    alias Curiosum.Authentication.Admin, as: Admin

    is the same as:

    alias Curiosum.Authentication.Admin

    I’ve seen thousands of Elixir aliases, and because of that, there is one tip I can give you: name the Elixir modules in your project in a way that you don’t have to use the as option at all. Here is an example of problematic module naming:

    defmodule App.User.Queries doend
    
    defmodule App.Admin.Queries doend 

    It’s problematic because you can’t use alias both modules without the as option. To fix that, always make the last part of the Elixir module name unique within your project:

    defmodule App.UserQueries doend
    
    defmodule App.AdminQueries doend 

    In real case scenario, you probably will work on a context with multiple data structures inside of it. Let's define another data structure in Curiosum.Authentication context:

    defmodule Curiosum.Authentication.User do
      defstruct [:id, :email]
    
      def changeset(user, params \\ %{}) do
        # implementation is not important here
      end
    end

    With two data structures defined, we want to use both of them in Curiosum.Authentication context. Of course, we also want to alias both of these:

    defmodule Curiosum.Authentication do
      alias Curiosum.Authentication.Admin
      alias Curiosum.Authentication.User
    end

    There is also a nice shortcut for this use case:

    defmodule Curiosum.Authentication do
      alias Curiosum.Authentication.{ Admin, User }
    end

    If for some reason, you declare an Elixir alias and don't use it, you'll see a warning:

    iex(1)> defmodule Curiosum.Authentication do
    ...(1)>   alias Curiosum.Authentication.{ Admin, User }
    ...(1)> end
    warning: unused alias Admin
      iex:2
    
    warning: unused alias User
      iex:2

    A warning can be skipped with :warn option set to false:

    alias Curiosum.Authentication.{ Admin, User }, warn: false

    However, this should raise the question of whether giving an alias without using its benefits is a good idea (from my perspective, it's not, and there is a cool way to make the warnings gone from the app).

    One more thing to note is that you can only use Elixir alias in the same lexical scope. For example, an alias inside of Elixir module is not valid outside of it:

    defmodule Curiosum.Authentication do
      alias Curiosum.Repo
      alias Curiosum.Authentication.Admin
    
      def get_admins do
        Repo.all(Admin) # This is correct
      end
    end
    
    Repo.all(Admin) # Wrong lexical scope, an error will be raised!

    One of the features of the Elixir Module mechanism is avoiding naming issues. As presented in the above examples, modules might grow to pretty long names and that's why aliases are used in Elixir very frequently. You should get used to them.

    Elixir import

    Elixir aliases are great for shortening module names, but what if we use functions from a given module extensively and want to skip using the module name part?

    You can import all functions and macros from a given module with Elixir import/2 macro:

    import Curiosum.Authentication.Admin

    This directive imports all public functions and macros from the given module except the ones starting with an underscore, for example, __build__.

    Although it's very handy, you probably don't need to import everything. In most cases, you only need a couple of functions.

    You can import specific functions or macros with :only option:

    alias Curiosum.Authentication.Admin
    import Admin, only: [changeset: 2]
    
    changeset(Admin) # correct function call

    You can also import everything except a few of specific functions or macros with :except option:

    alias Curiosum.Authentication.Admin
    import Admin, except: [changeset: 2]
    
    changeset(Admin) # incorrect function call

    Notice that you have to specify the arity of functions or macro. This is due to how Elixir is designed. Functions with different arities are not the same.

    Therefore you can't import all changeset functions at once:

    import Admin, only: :changeset    # error will be raised
    import Admin, only: [:changeset]  # error will be raised

    You can, however, import only functions or macros at once:

    import Admin, only: :functions
    import Admin, only: :macros

    Just like an alias/2 the Elixir import/2 directive is also lexically scoped:

    defmodule Curiosum.Authentication do
      alias Curiosum.Authentication.Admin
      import Admin, only: [changeset: 2]
    
      ...
    end
    
    changeset(Admin) # Wrong lexical scope, an error will be raised!

    There is also :warn option in Elixir import, which is being used to silence the warning generated when none of the imported functions/macros is used:

    import Admin, warn: false

    Again, remember that you should not use Elixir's import when you don't need it and if you need only specific functions/macros, use :only option.

    How about using Elixir import for functions with the same name and arity from different modules?

    alias Curiosum.Authentication.{ Admin, User }
    import Admin
    import User
    
    changeset(%{})

    Since both changeset/2 functions accept any arguments, there will be ambiguous call conflict:

    (CompileError) function changeset/1 imported from both Curiosum.Authentication.User and Curiosum.Authentication.Admin, call is ambiguous

    This is worth remembering and a big argument against importing all functions/macros at once. Keep it in mind!

    Elixir require

    Public methods in Elixir modules are globally available. This is not true for macros.

    Let's declare custom_unless/2 macro in Conditional module:

    defmodule Conditional do
      defmacro custom_unless(clause, do: expression) do
        quote do
          if(!unquote(clause), do: unquote(expression))
        end
      end
    end

    Now, look at what happens when we use custom_unless/2 just like with public methods:

    iex> Conditional.custom_unless 2 < 1, do: IO.puts "It works!"
    
    ** (CompileError) iex: you must require Conditional before invoking the macro Conditional.custom_unless/2

    As you can see in the error message, we need to require the Conditional module to use its macros. Why?

    Macro function is being evaluated during compilation. If you want to use it, you need to compile it first. This is exactly what Elixir require/2 does.

    In our example, we need to compile the Conditional module first, and only then we can use the custom_unless/2 macro:

    iex> require Conditional
    Conditional
    iex> Conditional.custom_unless 2 < 1, do: IO.puts "It works!"
    It works!
    :ok

    You can also pass :as option to require/2, which works the same as in the case of alias/2:

    iex> require Conditional, as: Cond
    Conditional
    iex> Cond.custom_unless 2 < 1, do: IO.puts "It works!"
    It works!
    :ok

    Notice that Elixir require/2 differs from alias/2 since alias/2 does not automatically compile the given module, and therefore we can't use its macros.

    It's also different from import/2. In fact, import/2 uses require/2 in the background to compile a module in Elixir, but it also makes it possible to skip the module names when invoking a function.

    The lexical scope also applies to the Elixir require/2 statement.

    custom code defined easily access functions alias multiple modules module attributes generates code only the function duplicate other modules function definitions alias foo

    Elixir use

    One of the main ideas behind Elixir is that it should be extensible. The Elixir use statement brings an extension point to modules.

    Imagine that a couple of modules use the same dependencies and share common behaviour. You can leverage use to perform some sort of set-up on each module.

    The Elixir use macro allows you to inject any code in the current module.

    Let's assume that we use the Ecto library in our Curiosum.Authentication.* schemas. To provide a simple solution for common setup operations, we can create Curiosum.Authentication.Schema:

    defmodule Curiosum.Authentication.Schema do
      defmacro __using__(_opts) do
        quote do
          use Ecto.Schema
    
          import Ecto
          import Ecto.Changeset
          import Ecto.Query
    
          def changeset(struct, params \\ %{}) do
            # assuming that changeset will perform same operations
          end
        end
      end
    end

    The above code shows the __using__/1 macro. The whole magic happens inside of it. Any code you put into __using__/1 will be injected and executed inside of modules that use it:

    defmodule Curiosum.Authentication.Admin do
      use Curiosum.Authentication.Schema
    end

    Thanks to use Curiosum.Authentication.Schema, the whole code inside of __using__ has been injected into the Curiosum.Authentication.Admin module.

    In the background use macro is being compiled into:

    defmodule Curiosum.Authentication.Admin do
      require Curiosum.Authentication.Schema
      Curiosum.Authentication.Schema.__using__
    end

    The __using__/1 macro accepts an optional argument, which can be used inside of it as needed. We can observe that in Phoenix with Controllers and Views:

    use CuriosumWeb, :controller

    or

    use CuriosumWeb, :view

    Are there any risks involving Elixir use?

    As you already know, we can put any code into __using__/1. You might not be sure what happens behind the scenes when you use external libraries. This is why you should always read the documentation carefully and look at what's being done inside of __using__.

    That's all for now. If you have any questions, let me know in the comments below!

    difference between import and require multiple modules other modules function definitions custom code remote calls opt in current scope alias multiple modules only the function duplicate all the functions foo module definition easily access functions generates code custom code defined alias foo other module string alias translates import listsoftware reuse bar module list module alias math

    FAQ

    What is the purpose of using alias in Elixir?

    The alias in Elixir simplifies module names to improve code readability and reduce repetition. It allows you to refer to modules by shorter names or assign them different names within a given scope.

    How does import in Elixir enhance code usability?

    The import directive in Elixir allows you to bring functions and macros from a module into the current scope, so you can use them without the module's prefix, streamlining code and reducing clutter.

    When should you use require in Elixir?

    You should use require in Elixir to use macros from a module. Require ensures the module is compiled and available before its macros are used, making them accessible in the current lexical scope.

    What is the role of use in Elixir?

    The use keyword in Elixir is a macro that allows you to inject any code from another module into the current module, facilitating code reuse and the extension of module functionalities.

    What are the differences between alias, import, and require?

    Alias renames modules, import brings functions or macros into scope, and require enables the use of macros by ensuring their module is compiled.

    Can alias and import directives affect the lexical scope in Elixir?

    Yes, both alias and import are lexically scoped. Their effect is limited to the block or module where they are declared.

    How can you prevent warnings for unused aliases in Elixir?

    You can prevent warnings for unused aliases in Elixir by using the warn: false option when defining aliases, though it's generally better to avoid unused aliases.

    Szymon Soppa Web Developer
    Szymon Soppa Curiosum Founder & CEO

    Read more
    on #curiosum blog

    Why Disqus slows down your page

    Why Disqus slows down your page and how to fix it?

    There are a lot of pages using Disqus as a way to communicate with readers. It's a great comments widget that comes with a free plan included which is probably one of the main reasons for its success.

    It has, however, a drawback - using it causes an increase in page load time. In this article, you'll learn how to fix that with a simple hack.