📌

Giới Thiệu Elixir

Elixir là gì?

Elixir là ngôn ngữ lập trình hàm (functional programming) được xây dựng trên nền tảng Erlang VM (BEAM). Elixir kế thừa khả năng xử lý đồng thời, fault-tolerant và distributed systems của Erlang với syntax hiện đại, dễ học.

💡 Tại sao chọn Elixir?
• Xử lý đồng thời mạnh mẽ với Actor model
• Fault-tolerant - "Let it crash" philosophy
• Scalability tuyệt vời (WhatsApp, Discord sử dụng)
• Syntax clean, dễ đọc
• Phoenix Framework cho Web Development

Cài đặt Elixir

# macOS với Homebrew
brew install elixir

# Ubuntu/Debian
sudo apt update
sudo apt install elixir

# Kiểm tra version
elixir --version
# Elixir 1.16.0 (compiled with Erlang/OTP 26)

# Chạy Interactive Shell
iex

Hello World

# hello.exs
IO.puts("Xin chào, Elixir! 💧")

# Hoặc trong iex
iex> IO.puts("Hello, World!")
Hello, World!
:ok

Giải thích:
IO.puts/1: In ra console với newline
.exs: File script Elixir (interpreted)
.ex: File compiled Elixir
iex: Interactive Elixir shell

📚

Khóa Học Elixir Từ A Đến Z (20 Bài)

Functional Programming

📘 Immutability & Pure Functions

Elixir là ngôn ngữ functional - data là immutable (không thể thay đổi). Thay vì modify data, ta tạo ra bản copy mới.

Pattern Matching

Pattern matching là một trong những tính năng mạnh mẽ nhất của Elixir.

# Tuple pattern matching
{status, result} = {:ok, "Hello"}
IO.puts(status)  # :ok
IO.puts(result)  # Hello

# List pattern matching
[head | tail] = [1, 2, 3, 4, 5]
IO.puts(head)    # 1
IO.inspect(tail) # [2, 3, 4, 5]

# Function với pattern matching
defmodule Math do
  def zero?(0), do: true
  def zero?(_), do: false
end

Math.zero?(0)  # true
Math.zero?(5)  # false

Pipe Operator

Pipe operator |> giúp chain các function lại với nhau một cách elegant.

# Thay vì viết nested functions
result = String.upcase(String.trim("  hello world  "))

# Dùng pipe operator
result = "  hello world  "
  |> String.trim()
  |> String.upcase()

IO.puts(result) # "HELLO WORLD"

# Ví dụ phức tạp hơn
[1, 2, 3, 4, 5]
  |> Enum.map(&(&1 * 2))       # [2, 4, 6, 8, 10]
  |> Enum.filter(&(&1 > 5))    # [6, 8, 10]
  |> Enum.sum()                 # 24
💡 Best Practice: Pipe operator làm code dễ đọc hơn bằng cách biến data transformation thành một "pipeline" từ trên xuống dưới.
🔄

Concurrency & OTP

Processes

Elixir processes là lightweight, chạy trên BEAM VM. Có thể spawn hàng triệu processes mà không tốn nhiều memory.

# Spawn một process
pid = spawn(fn -> 
  IO.puts("Hello from process!")
end)

# Gửi message giữa các processes
parent = self()

spawn(fn ->
  send(parent, {:hello, "from child"})
end)

receive do
  {:hello, msg} -> IO.puts("Got: #{msg}")
after
  1000 -> IO.puts("Timeout!")
end

GenServer

GenServer là behaviour chuẩn để xây dựng stateful servers trong Elixir.

defmodule Counter do
  use GenServer

  # Client API
  def start_link(initial_value) do
    GenServer.start_link(__MODULE__, initial_value, name: __MODULE__)
  end

  def increment do
    GenServer.call(__MODULE__, :increment)
  end

  def get_value do
    GenServer.call(__MODULE__, :get)
  end

  # Server callbacks
  @impl true
  def init(initial_value) do
    {:ok, initial_value}
  end

  @impl true
  def handle_call(:increment, _from, state) do
    {:reply, state + 1, state + 1}
  end

  @impl true
  def handle_call(:get, _from, state) do
    {:reply, state, state}
  end
end

# Usage
{:ok, _pid} = Counter.start_link(0)
Counter.increment()  # 1
Counter.increment()  # 2
Counter.get_value()  # 2
⚠️ Lưu ý: GenServer là nền tảng cho OTP. Hiểu rõ GenServer giúp bạn xây dựng hệ thống fault-tolerant với Supervisors.
🌍

Ứng Dụng Thực Tế

Phoenix Framework - REST API

# lib/my_app_web/controllers/user_controller.ex
defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller

  alias MyApp.Accounts
  alias MyApp.Accounts.User

  def index(conn, _params) do
    users = Accounts.list_users()
    render(conn, :index, users: users)
  end

  def create(conn, %{"user" => user_params}) do
    case Accounts.create_user(user_params) do
      {:ok, user} ->
        conn
        |> put_status(:created)
        |> json(%{data: user})
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> json(%{errors: format_errors(changeset)})
    end
  end
end

Tạo project Phoenix: mix phx.new my_app --database postgres

🖥️

Playground - Thử Code Elixir

Viết code Elixir và nhấn ▶ Chạy (hoặc Ctrl+Enter) để xem kết quả ngay!

💧 Elixir Sẵn sàng

📤 Output

📝 Nhấn ▶ Chạy hoặc Ctrl+Enter để thực thi code