Compiling a Module
Learn how to define a module and organize functions inside it.
We'll cover the following
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
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 iniex
.
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