No history yet

Elixir Basics

Getting Started with Elixir

Elixir is a functional programming language built for creating applications that can handle many connections at once and won't easily crash. It runs on the Erlang virtual machine, which is famous for powering massive, reliable telephone systems. Think of it as having a battle-tested engine under the hood.

Unlike languages you might have seen before, Elixir is functional. This means it's built around the idea of transforming data with functions. Instead of changing data in place, you take data, pass it through a function, and get new data back. The original data is never changed, a concept called immutability. This approach helps prevent a whole class of bugs and makes code easier to reason about, especially when many things are happening at once.

Basic Building Blocks

Like any language, Elixir has a set of basic data types. Let's look at the most common ones.

# Numbers
123     # Integer
1.23    # Float

# Atoms - constants whose name is their value
:ok
:error

# Booleans and nil
true
false
nil

# Strings (Binaries)
"hello world"

# Lists - linked lists, good for recursion
[1, 2, "hello", :ok]

# Tuples - fixed-size containers, good for returning multiple values
{1, "hello", :ok}

# Maps - key-value pairs
%{ "name" => "Alex", "age" => 30 }

Atoms might seem strange at first, but they are used everywhere in Elixir to represent status, like :ok or :error. Strings in Elixir are powerful, and while they look simple, under the hood they are sequences of bytes called binaries. Lists and tuples look similar, but they have different purposes. Lists are for collections of items that can grow or shrink. Tuples are for holding a fixed number of related items together, often used for function return values.

The Power of Pattern Matching

One of Elixir's most powerful features is pattern matching. In many languages, the equals sign (=) is for assignment. In Elixir, it's a match operator.

The = operator in Elixir doesn't just assign a value. It asserts that the left side and the right side have the same shape and value. If they match, the operation succeeds. If not, it fails.

When a variable is on the left side, it behaves like assignment because a variable can match any value.

# This works, because `x` can match the value 10.
x = 10

But what if we try to match two values?

10 = 10 # This succeeds, because 10 matches 10.

# This will cause an error!
# 10 = 11

This becomes incredibly useful for pulling values out of data structures. This is also known as destructuring.

# Matching with a tuple
{ :ok, result } = { :ok, "Some data" }
# Now `result` is bound to "Some data"

# Matching with a list
[head | tail] = [1, 2, 3]
# `head` is now 1
# `tail` is now [2, 3]

# Matching with a map
%{ "name" => name } = %{ "name" => "Alex", "age" => 30 }
# `name` is now "Alex"

Pattern matching allows you to write declarative code that clearly states what you expect the data to look like.

Modules, Functions, and Pipes

In Elixir, all code lives inside modules. A module is a collection of related functions. You define a module with defmodule and a named function with def.

defmodule Math do
  # A function named 'add' that takes two arguments
  def add(a, b) do
    a + b
  end
end

# You can call it like this:
Math.add(5, 10)

Functions in Elixir are identified by their name and the number of arguments they take, known as arity. The add function above has an arity of 2, often written as add/2.

Finally, let's look at one of Elixir's most beloved features: the pipe operator (|>). The pipe operator takes the result of the expression on its left and passes it as the first argument to the function on its right. This allows you to chain functions together in a highly readable way.

Imagine you have a list of numbers. You want to keep only the even ones, then square each of them, and finally sum them up. Without the pipe operator, your code might look like a set of nested function calls.

# Let's assume these Enum functions exist:
numbers = [1, 2, 3, 4, 5]

# Nested calls are hard to read from inside out.
sum(map(filter(numbers, is_even?), square))

The pipe operator transforms this into a clear, step-by-step pipeline.

# Using the pipe operator
numbers = [1, 2, 3, 4, 5]

# The data flows from one function to the next.
result = numbers
|> Enum.filter(&(&1 |> rem(2) == 0))
|> Enum.map(&(&1 * &1))
|> Enum.sum()

# result will be 20 (from 2*2 + 4*4)

This reads like a series of transformations: take the numbers, then filter them, then map over them, then sum the result. It's one of the key features that makes Elixir code so enjoyable to write and read.

Time to check your understanding.

Quiz Questions 1/5

What is a primary reason Elixir, running on the Erlang VM, is well-suited for building applications like chat servers or social media feeds?

Quiz Questions 2/5

In Elixir, the concept that data cannot be changed after it's created is called:

With these basics of immutable data, pattern matching, and functions, you have a solid foundation for exploring the world of Elixir.