Вот и наступил 2015 год. Подводя итоги прошлого года можно отметить две мэйнстримовые тенденции:
- Статическая типизация
- Функциональное программирование
Сегодня пойдет речь о втором пункте.
Год перемен
2014 год однозначно был функциональным. Вторую жизнь получили многие забытые функциональные языки, много новых появилось. Их начинали пихать куда можно и куда нельзя, где уместно их использовать, а где нет. Меня тоже немного задела эта волна. Совершенно случайно необходимо было написать слой приложения на любом языке кроме ruby. Задача выбора языка сейчас очень сложна из-за их огромного выбора. Но не на этот раз! Все больше и больше информации появлялось о новом молодом языке elixir. Он привлекал еще больше внимания, т.к. его создатель - Rails Core Team Member (Jose Valim)!. И как раз вышла стабильная версия 1.0.0! И он как раз еще и функциональный язык! Бинго! Как раз есть возможность окунуться в мир ФП на реальном примере! Работка обещала быть интересной :-)
Другой мир
На первый взгляд все казалось намного проще. Но в самом начале был тупик… ООПшный тупик… Нет классов и, соответственно, нет наследования и всех плюшек ООП. Есть только функции и их можно объединять в модули и все. Совсем другой мир! Но не менее интересный) Но я не буду полностью все описывать, а остановлюсь на нескольких, как мне кажется, особо интересных особенностях.
Трансформация данных
Для тех, кто хочет познакомится с elixir и функциональным
программированием, обязательно стоит посмотреть доклад Дейва Томаса
(Dave Thomas) Elixir: Power of Erlang, Joy of Ruby.
Основная идея, которую я подчерпнул из данного доклада - трансформация данных.
Т.е., рассматривая какую-либо часть архитектуры как черный ящик, то на
вход подается данные_1
, а на выходе мы ожидаем данные_2
. Я думаю,
что это применимо и к ОПП подходу, но в ФП это более выражено и есть
даже специальный оператор!
Pipe-оператор
В elixir есть специальный оператор |>
, который называется
pipe-оператор, который выполняет операции над данными слева. Например:
1 2 |
|
Причем количество операций преобразования может быть несколько
1 2 |
|
Это очень напоминает оператор |
, используемый в bash.
А что в ruby?
Ruby полностью объектно-ориентированный язык, поэтому в основе лежат объекты и связь между объектами с помощью сообщений. Но сколько же раз вы видели подобный код:
1 2 3 |
|
Меня всегда он вводит в конфуз, т.к. с первого взгляда сложно понять, где входные данные, а где список операций обработки.
Давайте попробуем реализовать подобное в ruby. Мы будем использовать здесь monckeypatching (начиная с ruby 2.0 необходимо использовать refinements)!
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Вот чем вам не код, как в листинге 2? В отличии от листинга 3, здесь код
намного понятен с первого взгляда, т.к. сразу можно понять: слева
объект, над которым производятся трансформации, а далее идут операции.
Стоит отметить, что в данном примере все операции производятся над типом String
.
Если в цепочке обработки используются несколько типов данных, то необходимо патчить
все соответствующие классы.
А как же коллекции?
Pipe-оператор также отлично справляется и с коллекциями. Вот, например, код, который увеличивает каждый элемент в массиве на 1:
1 2 3 4 5 6 7 8 |
|
Или даже лучше так:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
В ruby это выглядело бы следующим образом:
1 2 3 4 5 6 |
|
Или в стиле elixir’a (для этого будем использовать магический метод
Kernel#method
):
1 2 3 4 5 6 |
|
Что в итоге?
Я очень люблю ruby! Каждый день для себя открываю все больше и больше возможностей и не перестаю восхищатся его мощью и выразительностью. Но всегда не покидает чувство, что чего-то не хватает и ты находишь это в другом языке и пытаешься реализовать в ruby. Кто знает, возможно лет через N это запилят и в ruby :-)