Думаю, многие видели такие мемы. Но не дайте себя обмануть, в функциональном программировании паттерны тоже присутствуют, и те самые "моноиды в пространстве эндофункторов" являются ничем иным как паттернами. Некоторые из них все мы используем, даже об этом не подозревая, поэтому все обстоит немного проще, чем кажется.
Хотите пост про паттерны в функциональном программировании?
P.S.: когда починю проводку, вдогонку прилетит разбор RAG🙏🏾
Хотите пост про паттерны в функциональном программировании?
P.S.: когда починю проводку, вдогонку прилетит разбор RAG🙏🏾
👍4❤1🔥1
🛤 ROP – обработка ошибок без исключений и null
Представь: ты пишешь код, а в нём — сплошные
Проблема: необходимость обработки ошибок с каждого звена цепочки
Допустим, у нас есть типичный процесс регистрации пользователя. Нам нужно:
- провалидировать данные,
- проверить, нет ли уже такого пользователя,
- создать запись,
- отправить письмо для подтверждения.
На
Каждый шаг — это потенциальное ветвление. Каждая ошибка обрабатывается вручную.
А теперь посмотрите, как ту же логику можно выразить в функциональном стиле с использованием ROP:
Никаких
Но как это работает? Как мы обрабатываем ошибки?
Всё дело в том, что каждая операция может быть либо на «рельсе успеха», либо на «рельсе ошибки».
Что за рельсы?
Честно признаться, чтобы добиться такого же результата, что и в примере на
Здесь в игру вступает pattern matching — одна из самых мощных возможностей функциональных языков. Это достойно отдельного поста, поскольку одна эта вещь заменяет
Обрати внимание: у
- Если пришло
- Если пришло
Таким образом, ошибка, возникшая на любом этапе, «проскакивает» все следующие функции и доходит до
Полный пример: регистрация пользователя
Теперь давай соберём всё вместе:
Что в сухом остатке?
Представь: ты пишешь код, а в нём — сплошные
if-else, проверки статусов, возвраты ошибок на каждом шагу. Знакомо? Как будто хочется что-то исправить, но редко на ум приходит что-то такое, что можно было бы использовать где угодно, где есть какие-то цепочки вызовов. Прошу любить и жаловать – Railway-Oriented Programming!Проблема: необходимость обработки ошибок с каждого звена цепочки
Допустим, у нас есть типичный процесс регистрации пользователя. Нам нужно:
- провалидировать данные,
- проверить, нет ли уже такого пользователя,
- создать запись,
- отправить письмо для подтверждения.
На
Javascript это может выглядеть так:if (!validateData(registrationRequest))
return {status: "error", reason: "Данные не валидны"}
if (checkExistance(registrationRequest))
return { status: "error", reason: "Пользователь с такими данными уже существует" }
const user = createUser(registrationRequest)
if (!user)
return { status: "error", reason: "Не удалось создать пользователя" }
if (!sendEmailVerification(user))
return { status: "error", reason: "Не удалось отправить письмо" }
Каждый шаг — это потенциальное ветвление. Каждая ошибка обрабатывается вручную.
А теперь посмотрите, как ту же логику можно выразить в функциональном стиле с использованием ROP:
registration_request
|> validate_data()
|> check_existence()
|> create_user()
|> send_email_verification()
|> handle_error()
Никаких
if! Никаких промежуточных переменных! Абсолютная читаемость и понятность процесса. Поток данных последовательно передается от начала к концу, и глаз просто скользит сверху вниз.Но как это работает? Как мы обрабатываем ошибки?
Всё дело в том, что каждая операция может быть либо на «рельсе успеха», либо на «рельсе ошибки».
Что за рельсы?
Честно признаться, чтобы добиться такого же результата, что и в примере на
Javascript, нужно немного «достроить» пайплайн. Добавим несколько вспомогательных функций:defp bind({:ok, value}, func), do: func.(value)
defp bind({:error, reason}, _), do: {:error, reason}
defp map({:ok, value}, func), do: {:ok, func.(value)}
defp map({:error, reason}, _), do: {:error, reason}
defp handle_error({:error, reason}), do: IO.puts(reason)
defp handle_error(_), do: :okЗдесь в игру вступает pattern matching — одна из самых мощных возможностей функциональных языков. Это достойно отдельного поста, поскольку одна эта вещь заменяет
if и позволяет выполнять распаковку объектов, перегружать функции и еще много всего интересного. Обрати внимание: у
bind и map есть по две перегруженные версии (клаузы). Elixir автоматически выбирает ту, которая соответствует переданным данным.- Если пришло
{:ok, data} – остаемся на «рельсе успеха», применяем функцию и идём дальше.- Если пришло
{:error, reason} – переходим на «рельс ошибки», просто прокидываем ошибку до самого конца.Таким образом, ошибка, возникшая на любом этапе, «проскакивает» все следующие функции и доходит до
handle_error.Полный пример: регистрация пользователя
Теперь давай соберём всё вместе:
defp validate_data(data) do
# Логика валидации
# Если данные валидны, то возвращаем {:ok, data}
# Если не валидны – {:error, :invalid_data}
end
defp check_existence(data) do
# Логика проверки существования пользователя
# Данные свободны – {:ok, data}
# Иначе – {:error, :already_exist}
end
defp create_user(data) do
# Логика создания нового пользователя
# Удачно – {:ok, %User{}}
# Неудачно – {:error, :creation_failed}
end
defp send_email_verification(user) do
# Логика отправки сообщения
# Удачно – {:ok, true}
# Неудачно – {:error, :email_not_sent}
end
def handle_request(request) do
request
|> map(&validate_data/1)
|> map(&check_existence/1)
|> map(&create_user/1)
|> bind(&send_email_verification/1)
|> handle_error()
end
Что в сухом остатке?
🔥4❤1👍1
- Читаемость: код читается как последовательность шагов, а не как дерево условий.
- Надёжность: ошибки обрабатываются централизованно и не «теряются» по пути.
- Композируемость: мы можем легко переиспользовать, добавлять или убирать шаги в пайплайне.
Railway-Oriented Programming можно описать одной фразой: «Успех идёт вперёд, ошибка — прямиком к выходу». Если однажды попали на «рельс ошибки», то нет ни шанса попасть обратно.
P.S.: Справедливости ради стоит сказать, что такой подход очень легко реализовать в
#functional #patterns
- Надёжность: ошибки обрабатываются централизованно и не «теряются» по пути.
- Композируемость: мы можем легко переиспользовать, добавлять или убирать шаги в пайплайне.
Railway-Oriented Programming можно описать одной фразой: «Успех идёт вперёд, ошибка — прямиком к выходу». Если однажды попали на «рельс ошибки», то нет ни шанса попасть обратно.
P.S.: Справедливости ради стоит сказать, что такой подход очень легко реализовать в
Javascript с помощью then – получается абсолютно то же самое, что и в примере на Elixir. Именно поэтому из всех популярных языков программирования мне нравится именно он – по своей природе он очень близок к функциональным.#functional #patterns
❤4🔥2
Вот он, первый пост из цикла о паттернах в функциональном программировании, и уже он не влез в одно сообщение🫣
Для следующих длинных постов буду искать площадку для хостинга, так как Telegraph мне всё таки не нравится.
Будет интересно почитать, что ты думаешь об этом подходе, и какие решения применял для централизованной обработки ошибок и выстраивания цепочек действий, поскольку с этой проблемой я сталкиваюсь очень часто
Для следующих длинных постов буду искать площадку для хостинга, так как Telegraph мне всё таки не нравится.
Будет интересно почитать, что ты думаешь об этом подходе, и какие решения применял для централизованной обработки ошибок и выстраивания цепочек действий, поскольку с этой проблемой я сталкиваюсь очень часто
❤3👍1
К каждому паттерну я буду прикреплять примеры использования, чтобы не ограничиваться местами абстрактными примерами. Использование ROP можете посмотреть здесь
GitHub
todo-elixir/lib/todo.ex at master · lsdrfrx/todo-elixir
Contribute to lsdrfrx/todo-elixir development by creating an account on GitHub.
❤4⚡1
Пропал на 3 недели, но не просто так – активно преисполнялся в Elixir. В субботу планирую постримить, но не могу выбрать направление:
– Супервизор процессов на Elixir — написать лёгкий менеджер, который будет управлять процессами: запускать, останавливать, перезапускать, мониторить, добавлять в автозапуск. systemd писать не собираюсь, но минималистичный аналог runit — почему бы и нет? Вместе разберемся, как под капотом работают системы инициализации в *nix и напишем свою маленькую, да не абы как, а в функциональном стиле, на Elixir
– Fullstack на Vue3 + Express.js — помогаю товарищу с дипломной работой. В планах: авторизация, чат, вебсокеты и медитативная вёрстка.
Какой путь выбрать — решать тебе. Буду очень рад делегировать выбор на тебя😃
Выбираем реакцией:
Супервизор – 👀
Диплом – 🌚
– Супервизор процессов на Elixir — написать лёгкий менеджер, который будет управлять процессами: запускать, останавливать, перезапускать, мониторить, добавлять в автозапуск. systemd писать не собираюсь, но минималистичный аналог runit — почему бы и нет? Вместе разберемся, как под капотом работают системы инициализации в *nix и напишем свою маленькую, да не абы как, а в функциональном стиле, на Elixir
– Fullstack на Vue3 + Express.js — помогаю товарищу с дипломной работой. В планах: авторизация, чат, вебсокеты и медитативная вёрстка.
Какой путь выбрать — решать тебе. Буду очень рад делегировать выбор на тебя😃
Выбираем реакцией:
Супервизор – 👀
Диплом – 🌚
🌚6👀3👏1
P.S.: нормально настроил OBS в плане баланса звука, запись со стрима будет🫡
🎉4
То чувство, когда большая задача пошла в релиз без правок ни с ревью, ни с тестирования:
This media is not supported in your browser
VIEW IN TELEGRAM
😁4❤1
Начинаю ориентировочно в 12:00. Буду рад всем желающим🙏🏾
https://www.twitch.tv/lsdrfrx
https://www.twitch.tv/lsdrfrx
Twitch
lsdrfrx - Twitch
versatile developer
this :: IO Diary
Пропал на 3 недели, но не просто так – активно преисполнялся в Elixir. В субботу планирую постримить, но не могу выбрать направление: – Супервизор процессов на Elixir — написать лёгкий менеджер, который будет управлять процессами: запускать, останавливать…
Стрим длился рекордные 4 часа 40 минут🫣
За это время успел накидать пару базовых компонентов, построить скелет фронтенда и сделать регистрацию и вход в аккаунт на стороне бэкенда. Попробовал новые для себя фреймворки Hono и Drizzle. Пока всё тяп-ляп, далее буду приводить все в красивый вид – как и структуру, так и визуал
Смотри запись тут: https://www.youtube.com/watch?v=jADfBMvaW4c
За это время успел накидать пару базовых компонентов, построить скелет фронтенда и сделать регистрацию и вход в аккаунт на стороне бэкенда. Попробовал новые для себя фреймворки Hono и Drizzle. Пока всё тяп-ляп, далее буду приводить все в красивый вид – как и структуру, так и визуал
Смотри запись тут: https://www.youtube.com/watch?v=jADfBMvaW4c
🔥3❤1🥰1