What Is an .EX File?
Elixir script
Elixir (.ex, .exs)
Overview
Elixir is a dynamic, functional programming language designed for building maintainable and scalable applications. Created by José Valim in 2011, Elixir runs on the Erlang Virtual Machine (BEAM) and leverages the battle-tested Actor model for massive concurrency. It combines the productivity of Ruby-like syntax with the reliability and fault tolerance of Erlang, making it ideal for distributed systems, real-time applications, and IoT platforms.
Technical Details
- File Extensions:
.ex(compiled modules),.exs(scripts) - MIME Type:
text/x-elixir - Category: Programming Language
- First Appeared: 2011
- Paradigm: Functional, concurrent, distributed
- Platform: Erlang Virtual Machine (BEAM)
Key Features
Actor Model Concurrency
- Lightweight processes (not OS threads)
- Message passing between processes
- Isolated process state
- "Let it crash" philosophy with supervision trees
Fault Tolerance
- Process isolation prevents cascade failures
- Supervisor processes restart failed children
- Hot code swapping without downtime
- Built-in error handling and recovery
Functional Programming
- Immutable data structures
- Pattern matching
- Higher-order functions
- Pipe operator for data transformation
Syntax Examples
Basic Elixir Constructs
# Variables and basic data types
name = "Elixir"
version = 1.15
is_awesome = true
# Atoms (symbols)
status = :ok
error = :error
# Functions
def greet(name) do
"Hello, #{name}!"
end
# Anonymous functions
square = fn x -> x * x end
add = &(&1 + &2)
# Function calls
greet("World")
square.(5)
add.(3, 4)
Pattern Matching
# Basic pattern matching
{:ok, result} = {:ok, 42}
[head | tail] = [1, 2, 3, 4, 5]
# Function pattern matching
def handle_response({:ok, data}) do
IO.puts("Success: #{data}")
end
def handle_response({:error, reason}) do
IO.puts("Error: #{reason}")
end
# Case expressions
def classify_number(x) do
case x do
0 -> :zero
n when n > 0 -> :positive
n when n < 0 -> :negative
end
end
# With expressions for complex matching
def process_user(user_data) do
with {:ok, name} <- Map.fetch(user_data, :name),
{:ok, email} <- Map.fetch(user_data, :email),
true <- String.contains?(email, "@") do
{:ok, %{name: name, email: email}}
else
:error -> {:error, "Missing required field"}
false -> {:error, "Invalid email format"}
end
end
Data Structures
# Lists (linked lists)
numbers = [1, 2, 3, 4, 5]
names = ["Alice", "Bob", "Charlie"]
# List operations
[1 | [2, 3, 4]] # Prepend element
[1, 2] ++ [3, 4] # Concatenate lists
Enum.map(numbers, &(&1 * 2)) # Transform elements
# Tuples (fixed-size)
point = {10, 20}
person = {"John", 30, "Engineer"}
# Maps (key-value)
user = %{name: "Alice", age: 28, city: "Portland"}
config = %{"host" => "localhost", "port" => 4000}
# Updating maps
updated_user = %{user | age: 29}
new_field = Map.put(user, :email, "alice@example.com")
# Keyword lists (ordered key-value pairs)
options = [name: "MyApp", port: 4000, ssl: true]
Control Flow and Pipes
# Conditional expressions
if user.age >= 18 do
"Adult"
else
"Minor"
end
# Unless
unless user.banned do
"Access granted"
end
# Cond for multiple conditions
cond do
age < 13 -> "Child"
age < 20 -> "Teenager"
age < 60 -> "Adult"
true -> "Senior"
end
# Pipe operator for data transformation
"hello world"
|> String.upcase()
|> String.split()
|> Enum.map(&String.reverse/1)
|> Enum.join(" ")
# Result: "OLLEH DLROW"
# Complex data processing pipeline
data
|> Enum.filter(&(&1.active))
|> Enum.map(&transform_user/1)
|> Enum.group_by(&(&1.department))
|> Enum.map(fn {dept, users} -> {dept, length(users)} end)
Modules and Structs
Module Definition
defmodule Calculator do
@moduledoc """
A simple calculator module.
"""
@pi 3.14159
def add(a, b), do: a + b
def multiply(a, b), do: a * b
def circle_area(radius) do
@pi * radius * radius
end
# Private function
defp validate_positive(n) when n > 0, do: :ok
defp validate_positive(_), do: {:error, "Must be positive"}
end
Structs (Custom Data Types)
defmodule User do
defstruct [:name, :email, :age, active: true]
def new(name, email, age) do
%User{name: name, email: email, age: age}
end
def deactivate(user) do
%{user | active: false}
end
def adult?(user) do
user.age >= 18
end
end
# Using structs
user = User.new("Alice", "alice@example.com", 25)
inactive_user = User.deactivate(user)
is_adult = User.adult?(user)
Processes and Concurrency
Spawning Processes
# Simple process
pid = spawn(fn ->
IO.puts("Hello from process #{inspect(self())}")
receive do
message -> IO.puts("Received: #{message}")
end
end)
# Send message to process
send(pid, "Hello, process!")
# Process with state
defmodule Counter do
def start_link(initial_value \\ 0) do
spawn_link(fn -> loop(initial_value) end)
end
defp loop(current_value) do
receive do
{:get, caller} ->
send(caller, current_value)
loop(current_value)
{:increment, caller} ->
new_value = current_value + 1
send(caller, new_value)
loop(new_value)
{:decrement, caller} ->
new_value = current_value - 1
send(caller, new_value)
loop(new_value)
end
end
end
# Using the counter
counter = Counter.start_link(10)
send(counter, {:get, self()})
receive do
value -> IO.puts("Current value: #{value}")
end
GenServer (Generic Server)
defmodule GameScore do
use GenServer
# Client API
def start_link(initial_score \\ 0) do
GenServer.start_link(__MODULE__, initial_score, name: __MODULE__)
end
def get_score do
GenServer.call(__MODULE__, :get_score)
end
def add_points(points) do
GenServer.cast(__MODULE__, {:add_points, points})
end
def reset_score do
GenServer.cast(__MODULE__, :reset)
end
# Server Callbacks
@impl true
def init(initial_score) do
{:ok, initial_score}
end
@impl true
def handle_call(:get_score, _from, score) do
{:reply, score, score}
end
@impl true
def handle_cast({:add_points, points}, score) do
{:noreply, score + points}
end
@impl true
def handle_cast(:reset, _score) do
{:noreply, 0}
end
end
# Usage
{:ok, _pid} = GameScore.start_link(100)
GameScore.add_points(50)
current_score = GameScore.get_score() # 150
Web Development with Phoenix
Phoenix Controller
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
def index(conn, _params) do
users = Accounts.list_users()
render(conn, "index.html", users: users)
end
def show(conn, %{"id" => id}) do
user = Accounts.get_user!(id)
render(conn, "show.html", user: user)
end
def create(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "User created successfully.")
|> redirect(to: Routes.user_path(conn, :show, user))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
end
Phoenix LiveView (Real-time UI)
defmodule MyAppWeb.CounterLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, :count, 0)}
end
def render(assigns) do
~H"""
<div>
<h1>Counter: <%= @count %></h1>
<button phx-click="increment">+</button>
<button phx-click="decrement">-</button>
<button phx-click="reset">Reset</button>
</div>
"""
end
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
def handle_event("decrement", _params, socket) do
{:noreply, update(socket, :count, &(&1 - 1))}
end
def handle_event("reset", _params, socket) do
{:noreply, assign(socket, :count, 0)}
end
end
Database Operations with Ecto
Schema Definition
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :email, :string
field :age, :integer
field :active, :boolean, default: true
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :age, :active])
|> validate_required([:name, :email])
|> validate_format(:email, ~r/@/)
|> validate_number(:age, greater_than: 0)
|> unique_constraint(:email)
end
end
Database Queries
import Ecto.Query
alias MyApp.{Repo, Accounts.User}
# Basic queries
all_users = Repo.all(User)
user = Repo.get(User, 1)
user_by_email = Repo.get_by(User, email: "alice@example.com")
# Query composition
active_users =
User
|> where([u], u.active == true)
|> where([u], u.age >= 18)
|> order_by([u], u.name)
|> Repo.all()
# Aggregations
user_count =
User
|> where([u], u.active == true)
|> Repo.aggregate(:count, :id)
# Creating and updating
changeset = User.changeset(%User{}, %{name: "Bob", email: "bob@example.com", age: 30})
{:ok, user} = Repo.insert(changeset)
updated_changeset = User.changeset(user, %{age: 31})
{:ok, updated_user} = Repo.update(updated_changeset)
Testing
ExUnit Testing Framework
defmodule CalculatorTest do
use ExUnit.Case
doctest Calculator
test "addition works correctly" do
assert Calculator.add(2, 3) == 5
assert Calculator.add(-1, 1) == 0
end
test "multiplication works correctly" do
assert Calculator.multiply(3, 4) == 12
assert Calculator.multiply(0, 5) == 0
end
describe "circle_area/1" do
test "calculates area correctly" do
area = Calculator.circle_area(5)
assert_in_delta area, 78.54, 0.01
end
end
end
# Async testing for processes
defmodule CounterTest do
use ExUnit.Case, async: true
test "counter increments correctly" do
{:ok, counter} = Counter.start_link(0)
Counter.increment(counter)
Counter.increment(counter)
assert Counter.get_value(counter) == 2
end
end
OTP (Open Telecom Platform)
Supervision Trees
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
MyApp.Repo,
MyAppWeb.Endpoint,
{Registry, keys: :unique, name: MyApp.Registry},
MyApp.GameSupervisor
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule MyApp.GameSupervisor do
use DynamicSupervisor
def start_link(_args) do
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def start_game(game_id) do
child_spec = {MyApp.Game, game_id}
DynamicSupervisor.start_child(__MODULE__, child_spec)
end
@impl true
def init(:ok) do
DynamicSupervisor.init(strategy: :one_for_one)
end
end
Development Tools
Build Tools
- Mix: Build tool and task runner
- Hex: Package manager
- ExUnit: Testing framework
- Dialyzer: Static analysis tool
IDEs and Editors
- VSCode with ElixirLS
- Emacs with Alchemist
- Vim with vim-elixir
- IntelliJ IDEA with Elixir plugin
Mix Tasks
# Create new project
mix new my_app
# Install dependencies
mix deps.get
# Run tests
mix test
# Start interactive shell
iex -S mix
# Generate documentation
mix docs
# Format code
mix format
Common Use Cases
Real-time Applications
- Chat applications
- Live notifications
- Collaborative editing
- Gaming backends
IoT and Embedded Systems
- Device communication
- Sensor data processing
- Edge computing
- Distributed sensor networks
Financial Services
- Payment processing
- Trading systems
- Risk analysis
- Fraud detection
Web Applications
- API backends
- Real-time dashboards
- Microservices
- Content management systems
Notable Applications
- Discord: Voice and text chat platform
- Pinterest: Web analytics
- Moz: SEO tools and analytics
- Bleacher Report: Sports news platform
- PepsiCo: E-commerce platform
Learning Resources
- "Programming Elixir" by Dave Thomas
- "Elixir in Action" by Saša Jurić
- "The Little Elixir & OTP Guidebook" by Benjamin Tan Wei Hao
- Official Elixir documentation and guides
- ElixirSchool.com tutorials
- Elixir Forum community
Elixir provides a powerful platform for building distributed, fault-tolerant applications that can handle millions of concurrent connections while maintaining excellent developer productivity and code maintainability.
File Information
Elixir script
Code
.ex, .exs
text/x-elixir
Related File Types
Other file types in the Code category you might also need:
Start Analyzing ELIXIR Files Now
Use our free AI-powered tool to detect and analyze Elixir script files instantly with Google's Magika technology.
⚡Try File Detection Tool