Elixir: use vs import

Elixir

Elixir Problem Overview


What's the difference between use and import?

> use is a simple mechanism for using a given module into the current context

https://hexdocs.pm/elixir/Kernel.SpecialForms.html#import/2

> Imports function and macros from other modules

Looks like one difference is import let's you pick out the specific functions/macros whereas use brings everything in.

Are there other differences? When would you use one over the other?

Elixir Solutions


Solution 1 - Elixir

import Module brings all the Functions and Macros of Module un-namespaced into your module.

require Module allows you to use macros of Module but does not import them. (Functions of Module are always available namespaced.)

use Module first requires module and then calls the __using__ macro on Module.

Consider the following:

defmodule ModA do
  defmacro __using__(_opts) do
    IO.puts "You are USING ModA"
  end

  def moda() do
    IO.puts "Inside ModA"
  end
end

defmodule ModB do
  use ModA

  def modb() do
    IO.puts "Inside ModB"
    moda()     # <- ModA was not imported, this function doesn't exist
  end
end

This will not compile as ModA.moda() has not been imported into ModB.

The following will compile though:

defmodule ModA do
  defmacro __using__(_opts) do
    IO.puts "You are USING ModA"
    quote do          # <--
      import ModA     # <--
    end               # <--
  end

  def moda() do
    IO.puts "Inside ModA"
  end
end

defmodule ModB do
  use ModA

  def modb() do
    IO.puts "Inside ModB"
    moda()            # <-- all good now
  end
end

As when you used ModA it generated an import statement that was inserted into ModB.

Solution 2 - Elixir

use is intended for injecting code into the current module, while import is used to, well, import functions for use. You can build a use implementation which automatically imports functions for example, as I do with Timex when you add use Timex to a module, take a look at timex.ex if you want to know what I mean, it's a very simple example of how to build a module which can be use'd

Solution 3 - Elixir

See «alias, require, and import» page from the elixir official getting started guide:

# Ensure the module is compiled and available (usually for macros)
require Foo

# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo

# Invokes the custom code defined in Foo as an extension point
use Foo

Require

Elixir provides macros as a mechanism for meta-programming (writing code that generates code).

Macros are chunks of code that are executed and expanded at compilation time. This means, in order to use a macro, we need to guarantee its module and implementation are available during compilation. This is done with the require directive.

In general a module does not need to be required before usage, except if we want to use the macros available in that module.

Import

We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name. For instance, if we want to use the duplicate/2 function from the List module several times, we can import it:

iex> import List, only: [duplicate: 2]
List
iex> duplicate :ok, 3
[:ok, :ok, :ok]

In this case, we are importing only the function duplicate (with arity 2) from List.

Note that importing a module automatically requires it.

Use

Although not a directive, use is a macro tightly related to require that allows you to use a module in the current context. The use macro is frequently used by developers to bring external functionality into the current lexical scope, often modules.

Behind the scenes, use requires the given module and then calls the __using__/1 callback on it allowing the module to inject some code into the current context. Generally speaking, the following module:

defmodule Example do
  use Feature, option: :value
end

is compiled into

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

Solution 4 - Elixir

With background from Python/Java/Golang languages, the import vs use was also confused for me. This will explain code reuse mechanism with some declarative languages examples.

import

In short, in Elixir, you don't need to import modules. All public functions can be accessed by full-qualified MODULE.FUNCTION syntax:

iex()> Integer.mod(5, 2)
1

iex()> String.trim(" Hello Elixir  ")
"Hello Elixir"

In Python/Java/Golang, you need to import MODULE before you can use functions in that MODULE, e.g Python

In []: import math

In []: math.sqrt(100)
Out[]: 10.0

Then what import in Elixir does might surprise you:

> We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name

https://elixir-lang.org/getting-started/alias-require-and-import.html#import

So if you want to type sqrt instead of Integer.sqrt, trim instead of String.trim, import will help

iex()> import Integer
Integer
iex()> sqrt(100)
10.0

iex()> import String
String
iex()> trim(" Hello Elixir    ")
"Hello Elixir"

This might cause problems for reading code and when there is name-conflicting so it is not recommended in Erlang (the language that influences Elixir). But there is no such convention in Elixir, you can use it at own-risk.

In Python, the same effect can be done by:

from math import * 

and it only recommended to use in some special scenarios / interactive mode - for shorter/faster typing.

use & require

What makes use/require different is that they relate to "macro" - the concept that does not exist in Python/Java/Golang... family.

You don't need to import a module to use its functions, but you need to require a module to use its macros:

iex()> Integer.mod(5, 3) # mod is a function
2

iex()> Integer.is_even(42)
** (CompileError) iex:3: you must require Integer before invoking the macro Integer.is_even/1
    (elixir) src/elixir_dispatch.erl:97: :elixir_dispatch.dispatch_require/6
iex()> require Integer
Integer
iex()> Integer.is_even(42) # is_even is a macro
true

Though is_even can be written as a normal function, it is a macro because: > In Elixir, Integer.is_odd/1 is defined as a macro so that it can be used as a guard.

https://elixir-lang.org/getting-started/alias-require-and-import.html#require

use, to excerpt from Elixir doc:

> use requires the given module and then calls the __using__/1 callback on it allowing the module to inject some code into the current context.

defmodule Example do
  use Feature, option: :value
end

is compiled into

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

https://elixir-lang.org/getting-started/alias-require-and-import.html#use

So writing use X is same as writing

require X
X.__using__()

use/2 is a macro, macro will transform code into other code for you.

You will want to use MODULE when you:

  • want to access its macros (require)
  • AND execute MODULE.__using__()

Tested on Elixir 1.5

Solution 5 - Elixir

use Module requires Module and also calls __using__ on it.

import Module brings Module functionality into current context, not just requires it.

Solution 6 - Elixir

Import

Makes all functions and macros from a given module accessible inside of the lexical scope where it's called. Keep in mind that in most cases you need only one or more functions/macros to be imported.

Example:

defmodule TextPrinter do
  import IO, only: [puts: 1]
  
  def execute(text) do
    puts(text)
  end
end

iex> TextPrinter.execute("Hello")
Hello
:ok

Use

This macro allows you to inject any code in the current module. You should be careful when using external libraries with use, since you might not be sure what exactly happens behind the scenes.

Example:

defmodule Printer do
  defmacro __using__(_opts) do
    quote do
      def execute(text) do
        IO.puts(text)
      end
    end
  end
end

defmodule TextPrinter do
  use Printer
end

iex> TextPrinter.execute("Hello")
Hello
:ok

Behind the scene code inside of __using__ has been injected into TextPrinter module.

By the way, there is more dependency handling instructions in Elixir.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUser314159View Question on Stackoverflow
Solution 1 - ElixirgreggregView Answer on Stackoverflow
Solution 2 - ElixirbitwalkerView Answer on Stackoverflow
Solution 3 - ElixirfetshView Answer on Stackoverflow
Solution 4 - ElixirHVNSweetingView Answer on Stackoverflow
Solution 5 - Elixirhagi-traggerView Answer on Stackoverflow
Solution 6 - ElixirszsoppaView Answer on Stackoverflow