Modules

In Elixir, we’ll be building modules to group related functions together.

We’ve already seen this in action when using previous modules, such as the String.

Looking below we see the module String has a function length defined on it.

String.length("hello world")

We can create custom modules as well using the defmodule macro.

Let’s say we want to create a Calc module. We can define it like so:

iex> defmodule Calc do
     end

The Calc module isn’t very interesting yet. We can define functions inside a module using the def keyword. Let’s define a double/1.

iex> defmodule Calc do
        def double(a) do
          a + a
        end
     end

We can then call the Calc.double/0 function just like we are calling any other module:

iex> Calc.double(2) # 4

Let’s crate a file and write our code in there instead. Let’s create calc.ex file

touch calc.ex

Now add the following code to the file.

defmodule Calc do
  def double(a) do
    a + a
  end
end

Type each of the following lines into the command line - one line at a time.

iex
c("calc.ex")
Calc.double(4)

Once we open iex we can use our c function - which stands for compile. This will compile our calc.ex file and allow us to access it inside of iex.

Module Attributes

There is a way for us to create constants in modules

defmodule Calc do
  @note "Fancy"
  def desc() do
    ~s(#{@note} calculator.)
  end
end

Structs

Structs are useful for handling known data structures.

They are extension built on top of maps. The keyword defstruct defines what fields the struct will have along with their default values.

Structs take the name of the module they are defined in.

defmodule Calc.Op do
  defstruct name: "add", a: 1, b: 1
end

We now have a Calc struct %Calc{}

Stucts provide a compile-time guarantee that only the fields defined in defstruct are allowed to exist in the struct.

Import

The import command allows us to bring in functions from other modules

Let’s take a look at the following example:

iex(1)> import List
List
iex(2)> last([1, 2, 3])
3
iex(3)> import Enum, only: [map: 2]
Enum
iex(4)>

Note that last is a function that is defined on List. What other functions might we be able to access on list?

As we can see in the example -import let’s us bring in a module, and then access any functions that are defined on that module.

Require

require ensures a module is compiled and loaded in another module

Example:

defmodule Adder do
  require Calc
  def add(a, b) do
    Calc.op({:add, a, b})
  end
end

require ensures that the entire Calc module is compiled and loaded so that we can access it inside of Adder.

Use

Injects Apis into modules

defmodule Calc do
  defmacro __using__(_) do
    quote do
      def op({:add, a, b}), do: a + b
      def op({:subtract, a, b}), do: a - b
      def op(_) do
        "Not implemented"
      end
    end
  end
end
defmodule Adder do
  use Calc
  def add(a, b) do
    op({:add, a, b})
  end
end

It allows us to use functionality from certain modules inside of another module. Note that we can access the Calc api inside of Adder.