Найти в Дзене
IDE Cat

Elixir и ошибки новичков

В Эликсире много возможностей для эффективного написания кода. Новички, осваивая язык, проникаются его различными возможностями и начинают их применять повсеместно. В статье речь пойдет про перегрузку функций с паттернматчингом аргументов Эликсир позволяет осуществлять перегрузку функций, как, например, Java. Достаточно объявить две одноименные функции в одном модуле, например, рекурсивный обход коллекции с умножением каждого элемента на 2. Разберем еще один пример: создадим функцию, которая сортирует разные типы данных, в данном случае — list и tuple. Я не просто так выделил фразу “разные типы данных”, ведь в первую очередь перегрузка функций появилась в типизированных языках и используется при передаче аргументов разных типов или разном количестве аргументов. Это нормальные случаи использования перегрузки функции и паттернматчинга. Теперь я хочу показать несколько примеров из реальных проектов, в которых паттернматчинг и перегрузка функций использовались во зло. Пример №1 Модуль Help
Оглавление

В Эликсире много возможностей для эффективного написания кода. Новички, осваивая язык, проникаются его различными возможностями и начинают их применять повсеместно.

В статье речь пойдет про перегрузку функций с паттернматчингом аргументов

Эликсир позволяет осуществлять перегрузку функций, как, например, Java. Достаточно объявить две одноименные функции в одном модуле, например, рекурсивный обход коллекции с умножением каждого элемента на 2.

-2

Разберем еще один пример: создадим функцию, которая сортирует разные типы данных, в данном случае — list и tuple.

-3

Я не просто так выделил фразу “разные типы данных”, ведь в первую очередь перегрузка функций появилась в типизированных языках и используется при передаче аргументов разных типов или разном количестве аргументов.

Это нормальные случаи использования перегрузки функции и паттернматчинга.

Теперь я хочу показать несколько примеров из реальных проектов, в которых паттернматчинг и перегрузка функций использовались во зло.

Пример №1

Модуль Helper, который в зависимости от контекста и переданного кода ошибки возвращает текстовое описание:

-4

В таком коде есть пара проблем:
1. Между определениями функций нет пустой строки — всё слилось в однонепонятночто ;)
2. При необходимости расширения придется добавлять копипасту функции;

Рефакторинг примера

-5

Вместо трёх функций у нас теперь одна, которая выполняет все те же функции, сосредотачивает всю логику в одном месте, выглядит просто и элегантно. Если необходимо, чтобы в случае передачи несуществующего action_name возвращалась ошибка — достаточно добавить guard, и перечислить все возможные значения, например:

-6

Пример №2

Есть сервис Cbr-xml-daily, дающий API для получения курсов валют ЦБ РФ. Соответственно все курсы валют представлены по отношению к рублю.

Пример полученных данных по курсу валют:

Формат: {Номинал} {Символ Валюты} = {Сумма в рублях}р
1 USD = 68.95р
1 EUR = 79.32р
100 JPY = 61.21р
и т.п.
В итоговом хеше ключ — символ валюты, значение — тоже хеш, где value — сумма в рублях, nominal — номинал

-7

В одном проекте есть модуль, который переводит сумму из валюты в рубли по полученному курсу.

-8

Обилие перегруженных с паттернатчингом функций “размазывает” логику на несколько реализаций, и, честно говоря, я не понимаю, как это все вместе работает. Но из названия ясно, что функция должна выполнять, это и стало толчком к рефакторингу.

Рефакторинг примера

На самом деле упрощенный вариант не так уж и прост, вся логика находится в одной функции, но усложнился алгоритм подсчета. Для коллег такая алгебра может показаться сложной, поэтому стоит добавить комментарий, описывающий алгоритм.

-9

Пример использования: переведем 10 долларов США в бразильские реалы

-10

Пример описания алгоритма: Так как все курсы валют представлены по отношению к рублю, то нам неизвестно отношение доллара к евро. Но мы можем это отношение высчитать, через рубли. Для корректной конвертации надо получить соотношение каждой валюты к рублю. Для этого надо поделить курс валюты на ее номинал.
Когда получили соотношения валют к рублю, надо получить их соотношения друг к другу и умножить на количество исходной валюты.

Хочу привести еще один пример, когда не стоит использовать паттернматчинг с перегрузкой функций.

Давайте решим задачу:

Необходимо реализовать геометрический модуль, благодаря которому можно вычислять площади следующих фигур: круг, прямоугольник и квадрат.

Задача звучит просто, и вы, возможно, сразу захотите написать модуль, в котором будет одна функция с несколькими реализациями. Давайте попробуем:

-11

Все работает, проверять не будем. Перейдем к проблемам:

  • Автодополнение не может подсказать, какие у нас есть реализации подсчета площади. Так же отображается 2 функции, вместо трех, из-за различного количества аргументов. Если бы у всех трех функций была бы одинаковая арность — отобразилась бы 1 функция;
-12

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

Альтернативное решение

Избавимся от паттерматчинга и перегрузки функций. Название функции должно отображать суть, абстрагировать нас от реализации и помогать писать программы, чтобы мы не тратили время на изучение сторонних функций и как они устроены.

Поэтому мы дадим функциям явные названия, которые будут отражать суть — площадь какой фигуры находим.

-13

Теперь автодополнение будет нам помогать. Мы сразу видим, площади каких фигур считает модуль и какие аргументы принимает функция.

-14

Если подытожить, то паттернматчинг с перегрузкой функций допустим, когда:
1. Функция принимает разное количество аргументов и контекст понятен без чтения исходного кода;

2. Тип передаваемых аргументов отличается, как в примере фукнции сортировки в начале статьи;

3. Необходима реализация рекурсии.