Last time we were talking about functions I have mentioned that named functions are living inside modules. Modules provide the namespace for functions and other things defined inside. Usually grouped by same meaning.

For example, Elixir has set of functions to work with enumerables defined in the Enum module. And IO module contains functions for handling Input/Output operations.

We define modules using defmodule keyword followed by its name and a bloc, which contains a body of the module.

defmodule FizzBuzz do
def print do
IO.puts "FizzBuzz"
end
end


To call a function inside module we use ModuleName.function_name format.

iex> FizzBuzz.print
FizzBuzz


We can also define a module inside another module.

defmodule FizzBuzz do
defmodule MyIO do
def print(content) do
IO.puts content
end
end

def print do
MyIO.print "FizzBuzz"
end
end


and then call the function using the dot as a separator between module names.

iex> FizzBuzz.MyIO.print "Hello module"
Hello module


That means we can use the same format while we are defining nested module

defmodule FizzBuzz.MyIO do
def print(content) do
IO.puts content
end
end


It is worth to mention that the module name is an atom behind the scenes. For example, if we decide to represent our module name as a string, Elixir would prefix it with “Elixir.”

iex> to_string FizzBuzz.MyIO
"Elixir.FizzBuzz.MyIO"


That means we can use it to call the module in the following way:

iex> :"Elixir.FizzBuzz.MyIO".print "Module name is an atom"
Module name is an atom


## import

To eliminate duplication of existing functions we can use import directive to bring functions and macros of one module into our current one. That means we can reuse already defined functions within the current scope.

defmodule FizzBuzz do
def fizz do
"Fizz"
end

def buzz do
"buzz"
end
end

defmodule FizzBuzzUser do
import FizzBuzz

def fizz_buzz do
"#{fizz}#{buzz}"
end
end


We can see the result by calling our function

iex> FizzBuzzUser.fizz_buzz
"Fizzbuzz"


These functions will not be available to call outside of the module:

iex> FizzBuzzUser.fizz
** (UndefinedFunctionError) function FizzBuzzUser.fizz/0 is undefined or private. Did you mean one of:

* fizz_buzz/0

FizzBuzzUser.fizz()


It is allowed to import only (or except) some functions. Using following format:

import ModuleName, only: [function_name: arity, function_name: arity]
import ModuleName, except: [function_name: arity, function_name: arity]


We can also import only functions or only macros:

import ModuleName, only: :functions
import ModuleName, only: :macros


But that works only with only option, except is not allowed in that case.

Using only or except is recommended in order to avoid importing all the functions from that module.

## alias

We can use the alias directive to have an alias for a module, usually to minimize typing.

Can be used in the following format:

alias Very.Long.ModuleName, as: ShortName


The option :as can be omitted. In that case, the aliased name would be the same as the last part of the whole module name. From the example above it would be just ModuleName. So the following expressions are equivalent:

alias Very.Long.ModuleName, as: ModuleName
alias Very.Long.ModuleName


If you want to add aliases for the several modules from the same namespace, you can use the following format:

defmodule Very.Long.ModuleName1 do end
defmodule Very.Long.ModuleName2 do end

alias Very.Long.{ModuleName1, ModuleName2}


Let’s summarize it with an example

defmodule VeryLongMethodNameWhichIsAnoyingToType do
def name do
"Some name here"
end
end

defmodule AnotherModule do
alias VeryLongMethodNameWhichIsAnoyingToType, as: LongName

def name do
LongName.name
end
end

iex> AnotherModule.name
"Some name here"


An alias can be used inside function body in case you don’t want to expose it for the whole module.

There are two more directives require and use. They are more intended to work with macros. I will cover them when I talk about macros.

## Module attributes

We can define attributes in the module to serve as annotations and/or constants. They work only on the top level of the module and cannot be set inside a function. To define an attribute we are using attribute name prefixed by @.

defmodule MyApi do
@version 1

def api_version do
@version
end
end


We can define the same attribute multiple times, but that will affect on functions defined below it.

defmodule MyApi do
@version 1
def first_version, do: @version

@version 2
def second_version, do: @version
end

MyApi.first_version # => 1
MyApi.second_version # => 2


## Summary

Here we covered an introduction to the modules. Now we know how to define them, how to call functions inside the modules. We have also learned how to use directives for code reuse and module attributes. Regarding attributes, there is some interesting usage which I will cover later.