Перейти до основного вмісту

Керуючі конструкції в Elixir

Elixir пропонує різноманітні керуючі конструкції для управління потоком виконання програми. На відміну від імперативних мов, всі конструкції в Elixir повертають значення, що робить код більш виразним.

if та else

Конструкція if перевіряє умову і виконує код відповідно до результату:

# Базовий синтаксис
if true do
  "Це буде виконано"
else
  "Це не буде виконано"
end

# Однорядковий варіант
if connected?, do: "Підключено", else: "Відключено"

# Присвоєння результату
message = if age >= 18 do
  "Ви повнолітній"
else
  "Ви неповнолітній"
end

# Без else (повертає nil)
if user_logged_in? do
  show_dashboard()
end
Примітка: В Elixir тільки false і nil вважаються хибними. Всі інші значення (включаючи 0, "", []) вважаються істинними.

unless

Конструкція unless — це протилежність if, виконується коли умова хибна:

# Базовий синтаксис
unless paid? do
  send_invoice()
end

# З else (рідко використовується)
unless error do
  IO.puts("Успіх")
else
  IO.puts("Помилка")
end

# Однорядковий варіант
unless authenticated?, do: redirect_to_login()

# Зазвичай unless використовують без else
status = unless maintenance_mode? do
  :online
end
Увага: Не рекомендується використовувати unless з else, оскільки це зменшує читабельність. Краще використати if з інвертованою умовою.

case

Конструкція case дозволяє зіставляти значення з кількома зразками:

# Базове зіставлення
case File.read("config.json") do
  {:ok, content} ->
    parse_json(content)
  {:error, :enoent} ->
    "Файл не знайдено"
  {:error, reason} ->
    "Помилка: #{reason}"
end

# Зіставлення зі значеннями
result = case status do
  :ok -> "Успішно"
  :error -> "Помилка"
  :pending -> "Очікування"
  _ -> "Невідомий статус"
end

# З охоронцями (guards)
case number do
  n when n < 0 -> "Від'ємне"
  n when n == 0 -> "Нуль"
  n when n > 0 -> "Додатне"
end

# Зіставлення зі складними структурами
case user do
  %{role: :admin, active: true} ->
    grant_full_access()
  %{role: :user, active: true} ->
    grant_limited_access()
  %{active: false} ->
    deny_access()
  _ ->
    :unknown
end

cond

Конструкція cond перевіряє кілька умов послідовно, подібно до else-if в інших мовах:

# Базовий синтаксис
grade = cond do
  score >= 90 -> "A"
  score >= 80 -> "B"
  score >= 70 -> "C"
  score >= 60 -> "D"
  true -> "F"  # default випадок
end

# Перевірка кількох умов
status = cond do
  temperature > 30 -> :hot
  temperature > 20 -> :warm
  temperature > 10 -> :cool
  temperature > 0 -> :cold
  true -> :freezing
end

# Складні умови
message = cond do
  is_nil(user) ->
    "Користувач не знайдений"
  not user.active ->
    "Акаунт деактивовано"
  user.subscription_expired? ->
    "Підписка закінчилася"
  true ->
    "Ласкаво просимо!"
end
Примітка: В cond завжди потрібен випадок, що спрацює. Зазвичай останньою умовою ставлять true як default варіант.

with

Конструкція with ідеальна для послідовних операцій, які можуть завершитись помилкою:

# Базовий синтаксис
with {:ok, user} <- fetch_user(id),
     {:ok, profile} <- fetch_profile(user),
     {:ok, posts} <- fetch_posts(profile) do
  {:ok, %{user: user, profile: profile, posts: posts}}
end

# З обробкою помилок через else
with {:ok, file} <- File.read("data.json"),
     {:ok, data} <- JSON.decode(file),
     {:ok, validated} <- validate(data) do
  process(validated)
else
  {:error, :enoent} ->
    {:error, "Файл не знайдено"}
  {:error, :invalid_json} ->
    {:error, "Невалідний JSON"}
  error ->
    {:error, "Невідома помилка: #{inspect(error)}"}
end

# З проміжними обчисленнями
with {:ok, width} <- get_width(),
     {:ok, height} <- get_height(),
     area = width * height,
     true <- area > 0 do
  {:ok, area}
else
  false -> {:error, "Площа повинна бути додатною"}
  error -> error
end

# Практичний приклад: створення користувача
def create_user(params) do
  with {:ok, validated} <- validate_params(params),
       {:ok, hashed_password} <- hash_password(validated.password),
       {:ok, user} <- insert_user(validated, hashed_password),
       {:ok, _email} <- send_welcome_email(user) do
    {:ok, user}
  else
    {:error, :validation_failed} = error ->
      error
    {:error, :duplicate_email} ->
      {:error, "Email вже використовується"}
    error ->
      Logger.error("Помилка створення користувача: #{inspect(error)}")
      {:error, "Не вдалося створити користувача"}
  end
end

Порівняння конструкцій

Конструкція Призначення Коли використовувати
if/else Проста умова Коли є одна умова для перевірки
unless Негативна умова Коли потрібно виконати код при хибній умові
case Зіставлення зразків Коли потрібно зіставити значення з кількома зразками
cond Множинні умови Коли є кілька різних умов для перевірки
with Послідовні операції Для ланцюжків операцій з обробкою помилок

try, catch та rescue

Для обробки виключень використовуються конструкції try/catch/rescue:

# Обробка виключень через rescue
try do
  risky_operation()
rescue
  ArithmeticError ->
    "Математична помилка"
  RuntimeError ->
    "Помилка виконання"
  e in [ArgumentError, FunctionClauseError] ->
    "Помилка аргументів: #{inspect(e)}"
end

# Catch для throw
try do
  throw(:emergency_exit)
catch
  :emergency_exit -> "Екстрене завершення"
  value -> "Перехоплено: #{value}"
end

# З after для очищення ресурсів
{:ok, file} = File.open("data.txt", [:write])

try do
  IO.write(file, "Hello, World!")
after
  File.close(file)
end

# Повний приклад
result = try do
  dangerous_operation()
rescue
  e in RuntimeError ->
    Logger.error("Runtime error: #{inspect(e)}")
    {:error, :runtime_error}
catch
  :exit, reason ->
    Logger.error("Exit caught: #{inspect(reason)}")
    {:error, :exit}
after
  cleanup_resources()
end
Увага: В Elixir філософія "Let it crash" — краще дозволити процесу впасти і перезапуститися, ніж обробляти всі можливі помилки. Використовуйте try/rescue обережно!

Короткі форми (do:)

Всі конструкції можна писати в однорядковій формі:

# if/else
result = if valid?, do: :ok, else: :error

# unless
unless ready?, do: wait()

# case
status = case code, do: (200 -> :ok; 404 -> :not_found; _ -> :error)

# cond (рідко використовується в однорядковій формі)
result = cond do: (x > 0 -> :positive; true -> :other)

# Зазвичай однорядкову форму використовують для простих випадків
action = if authorized?, do: proceed(), else: halt()

Практичні поради

  • if/unless: використовуйте для простих бінарних умов
  • case: ідеальний для зіставлення зразків та обробки різних типів даних
  • cond: використовуйте замість вкладених if/else для множинних умов
  • with: незамінний для "railway-oriented programming" та обробки помилок
  • Зіставлення у функціях: часто краще використовувати кілька визначень функції замість case всередині функції
  • Уникайте вкладеності: використовуйте with або винесення логіки в окремі функції

Приклад комбінування конструкцій

defmodule UserService do
  def process_registration(params) do
    with {:ok, validated} <- validate(params),
         {:ok, user} <- create_user(validated) do
      case send_confirmation_email(user) do
        {:ok, _} ->
          {:ok, user}
        {:error, reason} ->
          Logger.warn("Email не надіслано: #{reason}")
          {:ok, user}  # Все одно повертаємо успіх
      end
    else
      {:error, :invalid_email} ->
        {:error, "Невалідний email"}
      error ->
        {:error, "Помилка реєстрації"}
    end
  end

  defp validate(params) do
    cond do
      is_nil(params[:email]) ->
        {:error, :invalid_email}
      String.length(params[:password]) < 8 ->
        {:error, :weak_password}
      true ->
        {:ok, params}
    end
  end
end

Правильне використання керуючих конструкцій робить код Elixir чистим, виразним та легким для розуміння. Обирайте конструкцію відповідно до контексту та складності логіки.

Коментарі

Популярні публікації

Встановлення Visual Studio Code на Ubuntu

Visual Studio Code (VS Code) — це потужний, легкий та кросплатформний редактор коду, який став стандартом для розробників завдяки універсальності та швидкості роботи. Він підтримує Windows, Linux і macOS, запускається швидше за повноцінні IDE, але водночас може перетворюватися на повнофункціональне середовище розробки завдяки тисячам розширень. VS Code має продуману архітектуру, інтуїтивний інтерфейс, вбудований термінал та інтелектуальну систему автодоповнення на базі Language Server Protocol, що забезпечує високоякісну підтримку синтаксису для багатьох мов програмування. Серед ключових можливостей — розумне автодоповнення коду (IntelliSense), навігація між класами, символами та файлами, рефакторинг, форматування коду, інтеграція з Git, підтримка віддаленої розробки через SSH або контейнерні середовища (Dev Containers). Завдяки відлагоджувачу (debugger), який налаштовується під будь-яку мову, розробник може запускати код у контрольованому середовищі, ставити брейкпоїнти, переглядат...

Angular CLI

CLI (command-line interface) – інтерфейс командного рядка. Перед початком роботи має бути встановлений Node.js Встановлення: npm install -g @angular/cli Отримання допомоги: ng help Буде приблизно такий результат: add Adds support for an external library to your project. analytics Configures the gathering of Angular CLI usage metrics. See https://angular.io/cli/usage-analytics-gathering. build (b) Compiles an Angular app into an output directory named dist/ at the given output path. Must be executed from within a workspace directory. deploy Invokes the deploy builder for a specified project or for the default project in the workspace. config Retrieves or sets Angular configuration values in the angular.json file for the workspace. doc (d) Opens the official Angular documentation (angular.io) in a browser, and searches for a given keyword. e2e (e) Builds and serves an Angular app, then runs end-to-end tests. extract-i18n (i18n-extract, xi18n) Extracts i18n mes...

Шпаргалка по базових командах PostgreSQL

1. Підключення до PostgreSQL через командний рядок: psql -h <host> -p <port> -U <username> -d <database> 2. Підключення до бази без параметрів (якщо користувач і база мають однакове ім’я): psql 3. Показати список усіх баз даних: \l 4. Підключитися до іншої бази даних: \c <database_name> 5. Показати список таблиць у поточній базі: \dt 6. Показати всі об'єкти (таблиці, індекси, секвенції): \d 7. Показати таблиці з усіх схем: \dt *.* 8. Переглянути структуру конкретної таблиці: \d <table_name> 9. Виконати SQL-запит (приклад): SELECT * FROM users; 10. Вийти з psql: \q 11. Створити нову базу даних: CREATE DATABASE mydb; 12. Створити нову таблицю: CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE ); 13. Додати новий запис: INSERT INTO users (name, email) VALUES ('Іван', 'ivan@example.com'); 14. Оновити дані в таблиці: UPDATE users SET name = 'Петро' WH...

Cron в Linux

cron — це механізм Linux для автоматичного виконання команд і скриптів за розкладом. Він використовується для регулярних завдань: резервного копіювання, очищення тимчасових файлів, запуску скриптів, перевірки сервісів тощо. Фоновий процес, який виконує заплановані завдання, називається cron daemon . У сучасних Linux-системах його часто реалізовано через cronie або сумісний cron-сервіс. Crontab Розклад завдань зберігається у crontab — таблиці cron-завдань. Переглянути завдання поточного користувача: crontab -l Відредагувати їх: crontab -e Для редагування системного crontab потрібні права адміністратора: sudo crontab -e Важливо: crontab -e і sudo crontab -e працюють з різними таблицями. Перша належить поточному користувачу, друга — користувачу root . Формат cron-запису Звичайний запис crontab має п'ять полів часу та команду: * * * * * команда Поля означають: * хвилина (0–59) * година (0–23) * день місяця (1–31) * місяць (1–12) * день тижня (0–7)...