Compiling a Module

Learn how to define a module and organize functions inside it.

Compiling in Elixir

Elixir always compiles and always executes source code. Both elixir and elixirc do both things. In addition to the Elixir file extension .ex, Elixir also supports .exs files for scripting. Elixir treats both files exactly the same way; the only difference is in the objective. The .ex files are meant to be compiled while .exs files are used for scripting. When executed, both extensions compile and load their modules into memory, although only .ex files write their bytecode to disk in the format of .beam files.

Once a program grows beyond a couple of lines, we want to structure it. Elixir makes this easy. We break our code into named functions and organize these functions into modules. In fact, in Elixir named functions must be written inside modules. We’ll look at a simple example to illustrate the concept of module, but before that, here are a few important steps we must discuss:

  • Compile the code through the iex -S mix command.

  • Run the required module and named functions through the given method: ModuleName.FunctionName.

To see the output on the terminal whenever we make any changes in the code, we must move out from the iex session by executing “Ctrl+C” twice, and we then proceed step 1.

Run Times.double(n) command in iex section for the code below where n can be any integer.

defmodule First.MixProject do
  use Mix.Project

  def project do
    [
      app: :first,
      version: "0.1.0",
      elixir: "~> 1.12",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end

  def hello do
    [
    IO.puts("Hello")
    ]
  end
end
Run the code above

If we want to incorporate new changes in the code, we follow the below steps:

  • Rerun the code using the “Run” button.
  • Exit from the current iex session by executing “Ctrl+C” twice.
  • Run the iex -S mix command. This loads the project with new changes again in iex.

Here, we have a module named Times. It contains a single function, double. We identify Elixir functions with the help of a number of arguments. Our function takes a single argument. So, we’ll see this function name written as double/1 if we make our function fail by passing it a string rather than a number.

iex> Times.double("cat")
** (ArithmeticError) bad argument in arithmetic expression
times.exs:3: Times.double/1

An exception (ArithmeticError) gets raised, and we see a stack backtrace. The first line tells us what went wrong (we tried to perform arithmetic on a string), and the next line tells us where. Notice what it writes for the name of function: Times.double/1.

In Elixir, a named function is identified by both its name and its number of parameters. Our double function takes one parameter, so Elixir knows it as double/1. If we had another version of double that took three parameters, it would be known as double/3. These two functions are totally separate as far as Elixir is concerned.

The function’s body is a block

The do...end block is one way of grouping expressions and passing them to other code. They are used in modules, named function definitions, and control structures (any place in Elixir where code needs to be handled as an entity).

However, do...end isn’t actually the underlying syntax. The actual syntax looks like this: def double(n), do: n * 2.

We can pass multiple lines to do by grouping them with parentheses.

def greet(greeting, name), do: (

IO.puts greeting
IO.puts "How're you doing, #{name}?"
)

The do...end form is just a lump of syntactic sugar. During compilation, it’s turned into the do: form. Typically, people use the do: syntax for single-line blocks and do...end for multiline ones. This means our Times example would probably be written as follows:

defmodule Times do
  def double(n), do: n * 2
end