If you programmed anything with Elixir (if not, check our introductory Elixir Interview) one of the first things you encounter are modules. But what exactly is a Module and how do they work internally?
Modules are one of the key building blocks of the elixir language and also of the BEAM, which is the runtime for elixir code. Simply put, a module is a collection of functions and attributes. All code that is executed must be loaded into the runtime at first. The code loading is done on a module level, meaning the runtime can only load modules. Therefore, all elixir code has to reside in a module.
In all examples the interactive elixir shell is used. If you want to follow along start one and type the following.
At first we created a module named
MyModule and a function named
myFunction that belongs to that module by using
defmodule command returns a tuple with four elements:
name of the module that was defined
a binary representation of the module called object code
the result of the last expression in the do block
Then we called the function
myFunction of the
MyModule module with the command
MyModule.myFunction and got
4711 as return value of the call.
defmodule command did two things.
It compiled our module definition into object code. That object code is a binary representation of the module that can be loaded by the runtime. Beside other things it contains:
The name of the module
The actual instructions that get executed by the runtime when a function is called. These instructions are called bytecode
A lookup table with all function names and a reference on where in the object code to find the function’s bytecode
It loaded the object code into the runtime.
MyModule.myFunction() was called the runtime, with the help of the lookup table, found and executed the bytecode for that function. In case of
myFunction that bytecode returns the literal
4711 to the caller.
Each module in Elixir is compiled seperatly resulting in one binary e.g. object code that includes the actual instructions for the runtime as well as as metadata in the form of module variables (we cover module variables later).
The next example will show how a module can be loaded from object code. Because
defmodule will not only create the object code but will also load it into the runtime and i want you to see how the loading happens seperatly, the object code has to be written to a file. To illustrate that
MyModule is loaded there is also an additional call to
The actual format of the object code does not matter at the moment what’s important is that each module has its own binary, which is object code.
In a newly opened iex
MyModule, as expected, is not available. After the object_code was read from the file and loaded with the
:code.load_binary function it becomes available.
In the code loading example the file in which the object code was stored was named
my_module_object_code.bin but if you rename that file to
my_module.beam it will get loaded by the iex on startup automatically. The
.beam filename makes the difference here. Every file that resides inside the directory you are in when executing the iex command and has a name that ends with
.beam is loaded on iex startup.
As you probably know elixir source code is stored in files that have names ending with
.ex or sometimes
Let’s do the same with the
MyModule module by creating a file
my_module.ex and putting the module definition in it.
Next we start a iex inside the same folder where the
my_module.ex file is located. When we try to execute our function like before we just get an error that informs us that the module
MyModule is not loaded.
c("my_module.ex", ".") compiles and the file and loads the module/s defined in it. Furthermore it writes a beam file to the folder provided as second parameter which is the current folder in the example.
A file with the name
Elixir.MyModule.beam was created and sure enough when the iex is restarted the module is available without the need to compile the elixir code again.
In practice all this is handled behind the scenes by mix the elixir build tool.
After considering the compile and runtime aspects of modules let’s explore (source-) code side of things next.
There are many ways you can use modules to structure your code and you can, and probably already have, read about this topic in many books, articles and so on. What i want to do is to show the basic building blocks which can be used to build more complex patterns for structuring code.
Contextualize functions with namespaces
Functions that, on an conceptual level, belong together can be put inside a module that then gives these functions context.
speek functions are called with
Farewill.speek() which gives them either the greet or the farewell context.
Another nice thing you can do, but that we will not discuss deeply here, is the creation of one data structure inside each module. These data structures called structs are often used to define the structure of the data on which the functions of the module operate.
Control the visibility of functions
This one is pretty simple. You can define functions to be callable from everywhere or only from functions that are part of the same module. Public functions are defined with
def and private functions are defined with
Inside the iex the call
Greet.speek("Hans") will return the expected result, but the call
will result in an exception
** (UndefinedFunctionError) function Greet.construct_message/1 is undefined or private Greet.construct_message("Hans")
Module attributes are the last thing you can put inside a module beside functions and structs. A module attribute is a key-value pair that attached to a module and can be set, accessed and to a degree modified, at compile time . This can be useful to define static values or pass additional data to the compiler. In this example the module attribute
message will be inlined and is available at runtime no more. The
moduledoc will be used by the compiler to add documentation to the object code. While the documentation is available at runtime the actual attribute is not.
To be precise there are module attributes that are also available at runtime and one can create module attributes with that property as well. This is however more cumbersome to write and only used for runtime related stuff. If you are not writing some kind of runtime or compiler you should not care about them and remember that module attributes are a compile time thing.
Modules are one of the basic building blocks of elixir. They are not only a language feature but also a basic building block of the BEAM runtime. They are essential for compilation, code loading and execution.
With this knowledge we can dive deeper into what elixir actually is by exploring how it is implemented. But that is for another time.