Elixir for beginners

April 10, 2024

Elixir for Beginners: An Introduction to Functional Programming

Welcome to the world of Elixir, a powerful functional programming language built on the Erlang VM. In this introduction, we’ll explore why learning Elixir is an excellent choice for beginners and set the stage for diving into its fundamentals.

Elixir offers a unique blend of simplicity, scalability, and concurrency support, making it an attractive language for developers of all skill levels. For beginners, Elixir provides an accessible entry point into the world of functional programming, offering a straightforward syntax and powerful abstractions for building robust and scalable software systems.

One of the key benefits of learning Elixir is its simplicity. With a clean and expressive syntax inspired by Ruby, Elixir makes it easy for beginners to write clean and readable code. This simplicity not only accelerates the learning curve but also promotes best practices in software development, such as code clarity and maintainability.

Scalability is another advantage of Elixir that beginners can benefit from. Elixir’s concurrency model, based on lightweight processes and message passing, allows developers to build highly concurrent and distributed systems with ease. This scalability ensures that Elixir applications can handle high loads and remain responsive even under heavy traffic.

Concurrency support is a fundamental aspect of Elixir programming that sets it apart from other languages. Elixir’s actor-based concurrency model enables developers to write concurrent code without the complexities of traditional threading models. This concurrency support simplifies the development of parallel and distributed systems, making Elixir an ideal choice for building real-time applications and distributed systems.

In the following sections, we’ll explore the fundamentals of Elixir programming for beginners, covering topics such as basic syntax, data types, functions, concurrency, and error handling. By the end of this guide, you’ll have a solid understanding of Elixir fundamentals and be well-equipped to start building your own Elixir applications. Let’s dive in!


Understanding Elixir


Overview of Elixir as a Functional Programming language

Elixir is a dynamic, functional programming language designed for building scalable and fault-tolerant applications. It combines the elegance and expressiveness of functional programming with the robustness and concurrency capabilities of the Erlang virtual machine (BEAM).


Origins of Elixir and relationship to Erlang

Elixir was created by José Valim and first released in 2012. It was designed to address some of the limitations of the Erlang language while leveraging its strengths. Erlang, developed by Ericsson in the 1980s, is known for its reliability, fault tolerance, and concurrency support, making it popular for building distributed and fault-tolerant systems (see Functional Programming success stories). Elixir builds upon Erlang’s concurrency model and leverages its ecosystem while providing a more modern and approachable syntax.


Key features of Elixir

Functional programming paradigm: Elixir follows a functional programming paradigm, emphasizing immutability, pure functions, and higher-order functions. This approach promotes code clarity, maintainability, and robustness by minimizing side effects and mutable state.

Lightweight processes: Elixir introduces lightweight processes as the primary concurrency model. These processes are lightweight, isolated units of execution that communicate through message passing. They are managed by the Erlang virtual machine, allowing Elixir applications to handle massive concurrency with minimal overhead.

Fault tolerance: Inspired by Erlang’s “let it crash” philosophy, Elixir embraces fault tolerance as a fundamental design principle. Elixir applications are designed to recover gracefully from errors and failures by isolating processes and restarting them when necessary. This approach ensures that applications remain responsive and resilient in the face of unexpected failures.


Elixir’s combination of functional programming principles, lightweight processes, and fault tolerance makes it well-suited for building distributed, fault-tolerant, and highly concurrent systems. It has gained popularity in various domains, including web development, telecommunications, and IoT (Internet of Things), and continues to grow as a powerful and versatile language in the software development landscape.


Getting Started with Elixir

Getting started with Elixir is a breeze, visit the official Elixir website and navigate to the “Install” section. There you will find installation per operating system.


Introducing the Elixir REPL (Interactive Shell)

Now that you have Elixir installed, let’s introduce you to the Elixir REPL, also known as the Interactive Shell. The REPL allows you to interactively run Elixir code and experiment with the language.

To start the Elixir REPL, simply open your terminal or command prompt and type iex (short for Interactive Elixir) and press Enter. You’ll see the Elixir prompt (iex(1)>) indicating that you’re now in the Elixir REPL environment.

From here, you can type any valid Elixir expression or function and press Enter to see the result. For example, try typing IO.puts(“Hello, Elixir!”) and pressing Enter. You should see the message “Hello, Elixir!” printed to the console.

The Elixir REPL is a great way to quickly test out Elixir code snippets and get comfortable with the language syntax. Experiment with different expressions and functions to get a feel for how Elixir works.

With Elixir installed and the REPL at your fingertips, you’re ready to start your journey into the world of Elixir programming! In the next section, we’ll dive into the basics of Elixir syntax and data types.


Basic syntax and data types

In this section, we’ll cover the basic syntax of Elixir and explore its core data types. Understanding these fundamentals is essential for writing Elixir code effectively.


Basic syntax

Elixir syntax is clean and expressive, making it easy to read and write code. Here are some key elements of Elixir syntax.

Variables: Variables in Elixir start with a lowercase letter or an underscore. They are immutable by default, meaning their value cannot be changed once assigned.

Atoms: Atoms are constants whose name is their value. They start with a colon (`:`) followed by a sequence of letters, digits, underscores, or at signs. Atoms are often used to represent symbols or states.

Literals: Elixir supports literals for representing basic data types such as integers, floats, strings, lists, tuples, and maps.


Core data types

Let’s explore the core data types in Elixir.

Integers: Integers represent whole numbers.
They can be written in decimal, hexadecimal, octal, or binary notation. For example:

number = 42
hexadecimal_number = 0x2A
binary_number = 0b101010


Floats: Floats represent floating-point numbers (numbers with decimal points).
They can be written with or without a decimal point. For example:

pi = 3.14159


Strings: Strings represent sequences of characters enclosed in double quotes.
They support interpolation and escape sequences. For example:

message = "Hello, Elixir!"
interpolated_message = "The value of x is #{x}"


Lists: Lists are ordered collections of elements enclosed in square brackets.
They can contain elements of any data type. For example:

numbers = [1, 2, 3, 4, 5]


Tuples: Tuples are ordered collections of elements enclosed in curly braces.
Unlike lists, tuples are fixed-size and can contain elements of any data type. For example:

point = {10, 20}


Maps: Maps are key-value pairs enclosed in %{}.
They allow for efficient lookups and support any data type as keys and values. For example:

user = %{name: "Alice", age: 30}



Here are some examples illustrating how to work with basic syntax and data types in Elixir:

# Variables
x = 42
y = :ok

# Integers
number = 123
hexadecimal_number = 0xFF
binary_number = 0b1010

# Floats
pi = 3.14159

# Strings
message = "Hello, Elixir!"
interpolated_message = "The value of x is #{x}"

# Lists
numbers = [1, 2, 3, 4, 5]

# Tuples
point = {10, 20}

# Maps
user = %{name: "Alice", age: 30}


These examples demonstrate the basic syntax and usage of core data types in Elixir. Experiment with them in the Elixir REPL to gain a deeper understanding of how they work. In the next section, we’ll explore functions and control flow in Elixir.


Functions and pattern matching

In Elixir, functions are the building blocks of code organization and encapsulation. They allow you to encapsulate behavior and reuse code throughout your programs. Additionally, Elixir provides powerful pattern matching capabilities, enabling you to match on data structures and control program flow effectively.


Functions in Elixir

Functions in Elixir are defined using the `def` keyword followed by the function name, parameters, and function body. Here’s a basic example of defining and calling a function in Elixir:

defmodule Math do
  def add(a, b) do
     a + b

# Calling the add function
IO.puts(Math.add(3, 5)) # Output: 8

In this example, we define a module Math with a function add that takes two parameters a and b and returns their sum.


Pattern matching

Pattern matching is a powerful feature of Elixir that allows you to destructure data and match on specific patterns. It’s commonly used in function definitions, case expressions, and other control flow constructs.


Let’s explore pattern matching with some examples:

# Matching function clauses
defmodule Greetings do
  def greet("Alice"), do: IO.puts("Hello, Alice!")
  def greet("Bob"), do: IO.puts("Hello, Bob!")
  def greet(name), do: IO.puts("Hello, #{name}!")

# Calling the greet function
Greetings.greet("Alice") # Output: "Hello, Alice!"
Greetings.greet("Bob")   # Output: "Hello, Bob!"
Greetings.greet("Eve")   # Output: "Hello, Eve!"

# Pattern matching in case expressions
case {1, 2, 3} do
  {1, x, 3} -> IO.puts("Matched: #{x}") # Output: Matched: 2
  _ -> IO.puts("No match")


In the Greetings module, we define multiple function clauses for the greet function, each matching on specific patterns of input data. When calling greet, Elixir matches the input against the defined patterns and executes the corresponding clause.

In the case expression, we match on a tuple {1, 2, 3} and extract the second element x using pattern matching. If the pattern matches, we print the value of x.

Pattern matching allows for concise and expressive code, making it easier to handle complex data structures and control flow in Elixir programs.



Functions and pattern matching are core features of Elixir that enable developers to write clean, readable, and maintainable code. By mastering these concepts, you’ll be well-equipped to build powerful and flexible Elixir applications. Experiment with functions and pattern matching in the Elixir REPL to gain a deeper understanding of how they work. In the next section, we’ll explore concurrency and processes in Elixir.


Concurrency and processes

Concurrency is a fundamental aspect of Elixir programming that allows multiple tasks to execute simultaneously, improving system responsiveness and performance. In Elixir, concurrency is achieved through lightweight processes, which are the building blocks of concurrent and distributed systems.


Concurrency in Elixir

Unlike traditional threading models, where each thread is associated with heavy system resources, Elixir implements concurrency using lightweight processes. These processes are independent units of execution that run concurrently and communicate with each other through message passing.


Lightweight processes

Elixir processes are extremely lightweight, with minimal memory overhead, making it feasible to spawn thousands or even millions of processes within a single Erlang VM instance. Each process operates in its own isolated memory space, ensuring fault tolerance and isolation.


Spawning processes

In Elixir, you can spawn a new process using the spawn/1 function, passing it a function to execute in the new process. Here’s a basic example:

pid = spawn(fn ->
  IO.puts("Hello from a new process!")


In this example, we spawn a new process that prints “Hello from a new process!” to the console.


Message passing

Elixir processes communicate with each other using message passing. Messages are sent asynchronously between processes and are stored in the recipient’s mailbox until processed. You can send messages to a process using the send/2 function and receive messages using the receive/1 construct.


Here’s an example demonstrating message passing between two processes:

# Process A sends a message to Process B
send(self(), {:hello, self()})

# Process B receives the message
receive do
  {:hello, sender_pid} -> IO.puts("Message received from #{inspect(sender_pid)}")


In this example, Process A sends a tuple {:hello, sender_pid} to Process B, where sender_pid is the process identifier of Process A. Process B receives the message and prints a message indicating that the message was received.



Concurrency and processes are fundamental concepts in Elixir programming, enabling developers to build highly responsive and scalable systems. By leveraging lightweight processes and message passing, Elixir applications can handle concurrent tasks efficiently while maintaining fault tolerance and isolation. Experiment with spawning processes and message passing in the Elixir REPL to gain a deeper understanding of how concurrency works in Elixir. In the next section, we’ll explore error handling and fault tolerance in Elixir.


Error handling and fault tolerance

Elixir is renowned for its robust error handling mechanisms and fault-tolerant design, making it well-suited for building resilient and reliable systems. In this section, we’ll explore Elixir’s approach to error handling and fault tolerance, including the concept of supervision trees.


Error handling in Elixir

In Elixir, errors are treated as normal events that can be gracefully managed and recovered from. Elixir encourages developers to embrace the “let it crash” philosophy, where processes are allowed to fail and be restarted rather than attempting to handle every possible error condition.


Supervision trees

Supervision trees are a key component of Elixir’s fault-tolerant design. A supervision tree is a hierarchical structure where processes are organized into supervision groups, with each group supervised by a higher-level supervisor. When a process fails, its supervisor can take corrective action, such as restarting the process, terminating it, or escalating the error to a higher-level supervisor.


Example of supervisors

Let’s consider an example of using supervisors to manage a group of worker processes:

defmodule MySupervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, [])

  def init(_) do
    children = [
     {MyWorker, 0}
    Supervisor.init(children, strategy: :one_for_one)


In this example, we define a supervisor module MySupervisor using the Supervisor module. We specify a strategy (:one_for_one) indicating that if any child process fails, only that process will be restarted.

We then define a worker process MyWorker, which specifies the module and arguments for the worker process. This worker process could represent any long-running computation, database connection, or external service.

By organizing processes into supervision trees and defining appropriate restart strategies, Elixir applications can automatically recover from failures and maintain system resilience.



Elixir’s approach to error handling and fault tolerance provides a robust foundation for building resilient and reliable systems. By embracing the “let it crash” philosophy and leveraging supervision trees, developers can build applications that gracefully recover from errors and maintain high availability. Experiment with supervision trees and error handling strategies in your Elixir applications to ensure they are robust and fault-tolerant. In the next section, we’ll conclude our exploration of Elixir for beginners.


Building your first Elixir application

Congratulations on reaching this stage! Now it’s time to apply what you’ve learned and build your first Elixir application from scratch. In this section, we’ll walk you through the process step by step.


Project Structure

First, let’s create a new directory for our Elixir project. Open your terminal or command prompt and navigate to the directory where you want to create your project. Then, run the following command to create a new Elixir project using Mix, Elixir’s build tool:

mix new my_first_app


This command will create a new directory called my_first_app containing the basic structure of an Elixir project.


Define modules and implement functionality

Next, let’s define some modules and implement basic functionality in our application. Open the lib directory within your project directory and navigate to the my_first_app subdirectory. You’ll find a file called my_first_app.ex containing a module definition.

Let’s define a simple module called Greeter and implement a function that greets the user:

defmodule MyFirstApp.Greeter do
  def greet(name) do
    IO.puts("Hello, #{name}!")


Save the file after making these changes.


Running and testing the application

Now, let’s run and test our application. Open your terminal or command prompt and navigate to your project directory (my_first_app). Then, start an interactive Elixir shell (REPL) by running the following command:

iex -S mix


Once in the Elixir REPL, you can load your module and call the `greet` function:

iex> MyFirstApp.Greeter.greet("Alice")


You should see the output “Hello, Alice!”. Congratulations, you’ve successfully run your first Elixir application!



In this section, we walked through the process of building your first Elixir application from scratch. We created a new Elixir project using Mix, defined modules and implemented basic functionality, and ran and tested our application using the Elixir REPL.

Now that you’ve built your first Elixir application, feel free to experiment with adding more functionality, exploring different Elixir features, and building more complex applications. Elixir’s powerful and expressive syntax makes it a joy to work with, and there’s much more to discover as you continue your journey with the language. Happy coding!


Advanced topics and next steps

Congratulations on completing your journey through the fundamentals of Elixir! Now that you have a solid foundation, it’s time to explore more advanced topics and continue your learning journey. In this section, we’ll provide an overview of some advanced topics in Elixir and suggest next steps for further exploration.


Advanced topics

OTP (Open Telecom Platform): OTP is a set of libraries, design principles, and best practices for building scalable and fault-tolerant systems in Elixir. Dive deeper into OTP to learn about GenServers, Supervisors, and other OTP behaviors.

Metaprogramming: Elixir’s metaprogramming capabilities allow you to write code that writes code. Explore macros, code generation, and DSLs (Domain Specific Languages) to enhance your Elixir applications.

Concurrency and parallelism: Learn advanced techniques for managing concurrency and parallelism in Elixir, including OTP supervisors, distributed Erlang, and distributed Elixir.

Web development with Phoenix: Phoenix is a powerful web framework for Elixir that enables rapid development of high-performance web applications. Explore Phoenix to build real-world web applications and APIs.

Testing and documentation: Discover advanced testing strategies and tools for ensuring the quality and reliability of your Elixir code. Learn about Elixir’s built-in testing framework ExUnit and tools like ExDoc for generating documentation.


Next steps

Read books and tutorials: There are many excellent books and online tutorials available for learning Elixir. Check out “Programming Elixir” by Dave Thomas and “Elixir in Action” by Saša Jurić for in-depth exploration of Elixir concepts and techniques.

Join elixir communities: Join Elixir communities such as the Elixir Forum and Elixir Slack to connect with other Elixir enthusiasts, ask questions, and share knowledge and experiences.

Contribute to open source: Contribute to Elixir open-source projects on GitHub to gain practical experience and collaborate with other developers in the Elixir community.

Build real-world projects: Apply your Elixir skills to real-world projects and challenges. Whether it’s building web applications, CLI tools, or backend services, there are endless opportunities to use Elixir in meaningful ways.

Attend conferences and meetups: Attend Elixir conferences (ElixirConf EU and ElixirConf US), meetups (Func Prog Sweden), and workshops to learn from industry experts, network with other developers, and stay up-to-date with the latest developments in the Elixir ecosystem.



Elixir is a powerful and versatile programming language with a vibrant and supportive community. As you continue your journey with Elixir, don’t be afraid to experiment, explore, and push the boundaries of what’s possible. Keep learning, building, and sharing your knowledge with others, and enjoy the journey ahead!



In conclusion, we’ve embarked on a journey through the fundamentals of Elixir, a powerful functional programming language built on the Erlang VM. Let’s recap the key points we’ve discussed in this blog post.

We began by introducing Elixir as a beginner-friendly language with a clean and expressive syntax, making it accessible for developers of all skill levels. We explored the benefits of learning Elixir, including its simplicity, scalability, and concurrency support, which make it an excellent choice for building robust and resilient software systems.

Throughout the blog post, we delved into various aspects of Elixir programming, including basic syntax and data types, functions and pattern matching, concurrency and processes, error handling and fault tolerance, and building your first Elixir application. We demonstrated how Elixir’s features and principles empower developers to write clean, maintainable, and fault-tolerant code.

As we conclude, it’s essential to reinforce the benefits of learning Elixir for beginners. By mastering Elixir, developers gain not only a powerful language for building scalable and fault-tolerant software but also valuable insights into functional programming paradigms and distributed systems design.

We encourage readers to dive deeper into Elixir and continue their journey towards mastering functional programming. Explore advanced topics such as OTP, metaprogramming, web development with Phoenix, and contribute to open-source projects to enhance your skills and knowledge.

Happy coding, and may your journey with Elixir be rewarding and fulfilling!


Additional resources

Check out the Ada Beat Functional Programming blog for more topics, including functional programming principles, summaries of MeetUps, language specific articles, and much more. Whether you’re interested in functional programming theory or practical application, we have something for everyone.