Враження від erlang після року роботи з ним, записки програміста
Почну з загальних слів. Багато хто помилково вважає, що Erlang - функціональна мова програмування загального призначення, свого роду Lisp з людським синтаксисом. У першому наближенні, це дійсно так. Але при кілька більш детальному розгляді виявляється, що Erlang - не функціональна, а скоріше об'єктно-орієнтована. і зовсім не мова програмування, а фреймворк для створення розподілених відмовостійких додатків. Ну або не дуже відмовостійких і не дуже розподілених, тут вже як напишіть. Чому фреймворк - зрозуміло, вся міць Erlang'а полягає в Open Telecom Platform, що включає в себе поведінки gen_server. gen_event і інші, а також ETS. Mnesia і так далі. Але чому Erlang раптом об'єктно-орієнтована?
Взагалі, що таке ООП. Це коли є об'єкти, і об'єкти для взаємодії між собою посилають один одному повідомлення. Зовсім як в Erlang, тільки об'єкти тут називаються процесами, а класи - модулями. В Erlang навіть є успадкування інтерфейсу, тільки тут це називається поведінкою (behaviour). Спадкування реалізації не передбачено, що, швидше за все, й на краще. Однак ніщо не заважає використовувати делегування. Насправді, Erlang куди сильніше відповідає ідеям ООП, ніж ті як би ООП мови, на яких сьогодні все пишуть.
Зрозуміло, при всьому при цьому Erlang залишається функціональним мовою. Просто це не зовсім те ФП, до якого ми звикли.
А тепер до суті. Що ж радує при програмуванні на Erlang?
- За рахунок локалізації побічних ефектів і незмінності змінних код на Erlang в рази простіше підтримувати, ніж код на якомусь імперативною мовою, де, до того ж, неодмінно використовуються п'ятиповерхові ієрархії класів і тп;
- Елегантність і логічність мови. Процеси повинні приймати які завгодно повідомлення, значить потрібна динамічна типізація. а не статична. Як рівномірно розподілити процесорний час між тисячами процесів? Зрозуміло, скомпілювати програму в байткод і використовувати лічильники виконаних інструкцій. І тд і тп, на все є логічні відповіді. Замислюючись над цим, розумієш, що Erlang не міг бути ніяким іншим;
- Модель акторів - це круто. Немає розділяється стану (майже) і немає дедлоков (майже);
- Зручне управління ресурсами. ETS, сокети і тп прив'язуються до процесу. Коли процес в силу тих чи інших причин вмирає, ресурс звільняється. Не так-то просто дати ресурсів витекти, хоча все ж можливо. Тут також не можна не відзначити правильність збирача сміття в Erlang. По-перше, він не на лічильниках посилань, як в деяких інших мовах. По-друге, пам'ять не тече при використанні деструкторов (бо їх немає), як це може статися в Python. Крім того, збірка сміття в одному з процесів не зупиняє всі інші процеси, як в якомусь Go;
- Швидкість вивчення. І справді, через пару тижнів роботи під чуйним керівництвом більш досвідчених колег починаєш писати цілком стерпний бойової код. А через пару місяців вже перестаєш відчувати себе Джуніором;
- Зрілість мови. Купа готових бібліотек і фреймворків, підтримка різними IDE (але я все одно пишу на Erlang в Vim), розвинений інструментарій (dbg. Сommon Test. Xref, Dialyzer, TypEr, Observer, ...). Багато хорошої літератури, придатні man pages;
- У моїй практиці ще ніколи не було так просто писати паралельний код. Розпаралелювання обчислень - моя улюблена оптимізація в цій мові. Якщо зайти на сервер з Erlang-нодою під великим навантаженням і подивитися в htop, видно, що всі ядра завантажені рівномірно. Притому, без особливих зусиль з нашого боку. Також, завдяки легковажним процесам можна, наприклад, без праці обслуговувати одночасно 10к TCP-з'єднань;
- В Erlang не потрібно перекручуватися, щоб зберегти який-небудь стейт в пам'яті. Кешування - це моя друга улюблена оптимізація в Erlang після розпаралелювання. У всяких там скриптових мовах для таких речей часто використовують Redis або Memcached. але ж ходіння в Redis істотно дорожче ходіння в свою оперативну пам'ять;
- Взагалі, в скриптових мовах постійно виникає потреба у всяких сторонніх додатках - Redis, Tarantool і інших NoSQL, а також FastCGI, всіляких SQL-проксі і інших милицях. В Erlang все необхідне або є «з коробки» (в тому числі в комплекті йдуть SSL, Gzip і інші корисності), або реалізуються в п'ять рядків коду;
- Кросплатформеність. Ви можете розробляти додаток під MacOS, а в продакт запускати його під Ubuntu. Віртуальна машина Erlang - це фактично самостійна операційна система;
- В Erlang можна викочувати хотфиксов в продакт без зупинки системи. Або в тестове оточення без переривання виконання тестів. Також за допомогою remsh можна причепитися до працюючої ноді і подивитися, що прямо зараз твориться в кишках, або, скажімо, поміняти настройки системи. Дуже зручна можливість;
- Мова розвивається, але при цьому примудряється залишатися стабільним. Ось в світі Scala. наприклад, все дуже швидко змінюється. Код на Scala, написаний пару років назад, сьогодні вже можна викидати на смітник. В Erlang теж викидають застарілі функції (як це було з багатьма функціями з модуля crypto) і можливості мови (параметризрвані модулі). Але все це афекту мізерну частину коду. А ще в R17 обіцяють додати map'и з BIF'амі для перетворення в / з JSON;
- Висока швидкість розробки, які не менше, ніж у скриптових мов;
- Грамотне і чуйне співтовариство програмістів. Існує небезпідставна думка, що вже якщо в компанії пишуть на Erlang, швидше за все, це хороша компанія;
- Є вакансії. Хороші. Порівняно багато для світу ФП;
Також з сильних сторін Erlang'а хотілося б згадати швидку компіляцію, захищене програмування «з коробки», легкість знаходження вузьких місць (я зазвичай шукаю в REPL тупо за допомогою timer: tc / 1) і подальшого їх усунення, а також всякі прикольні пріложенькі на Erlang, типу RabbitMQ і Riak. які взагалі-то можна використовувати звідки завгодно, але особливо сильно хочеться чомусь саме з Erlang.
Але у будь-якій бочці меду є ложка дьогтю. І, часом, не одна. Що дратує:
Але це все дурниця. Куди більш істотний недолік Erlang на мій погляд полягає в наступному. Всім нам відомо, що абстракції - це добре. Ми вводимо новий тип і функції для роботи з ним, ну або клас і методи, якщо вам так більше подобається. Ці функції або методи визначають інтерфейс, через який ми працюємо з даними, завдяки чому відбувається абстрагування від внутрішнього устрою типу. Для введення нових «типів» або «класів» в Erlang використовуються записи (records). Здавалося б, які проблеми - оголошуємо record і функції для роботи з ним, життя прекрасне і дивовижне. Але в Erlang це не працює! Розглянемо, наприклад, наступний код, поки що на Haskell:
myFynction x other args
| field1 x == 123 field2 x == 456 field3 x == 789 = -.
Чи можна написати щось подібне на Erlang? Виявляється, що не можна. Справа в тому, що в Клозе можна використовувати тільки чисті функції. А оскільки Erlang не контролює чистоту функцій на рівні типів або ще якось, всі функції, оголошені користувачем, вважаються брудними. Ось ерлангістам і доводиться користуватися інклуд і звертатися до полів записів безпосередньо якось так:
my_function # 40; # state # 123; field1 = 123. field2 = 456. field3 = 789 # 125 ;.
Other. Args # 41; ->%.
В результаті ламається абстракція, всі модулі все знають про пристрій записів і починають покладатися на ці знання. Якщо ми раптом вирішимо, наприклад, що field3 повинен не зберігатися, а постійно обчислюватися, доведеться переписати весь код, де відбувається звернення до field3. А в Haskell можна просто перевизначити field3 і все буде працювати, як раніше. Зрозуміло, що цю проблему можна обійти, але в результаті збільшується кількість шаблонного коду.
Взагалі, як ви, напевно, вже могли здогадатися, чим більше я пишу на Erlang, тим більше мені подобається Haskell. В Erlang ви можете помилково написати:
my_function # 40; # 123; stream = Stream # 125; # 41; ->%.
my_function # 40; # state # 123; stream = Stream # 125; # 41; ->%.
... і це успішно скомпілюється! А що такого? Нормальний кортеж, що містить один атом, який ми зв'язали зі змінною Stream. Або ось ще приклад:
my_function # 40; UserRole # 41;
when UserRole =: = admin;
UserRole =: = moderator ->%.
Що станеться, якщо ми захочемо ввести нову роль користувача, наприклад, super_moderator? Нічого особливого, код буде працювати. Не завжди матчів, але це вже дрібниці. А ось Haskell в такому випадку успішно знайде всі місця, де потрібно поправити код.
Але, незважаючи на всі названі шорсткості, Erlang - це хороший, придатний мову. Він відмінно підходить як для створення складних розподілених відмовостійких додатків, так і для звичайних сайтик. Нехай мову далекий від ідеалу, але це куди більш вдалий вибір. ніж всякі там Perl, Python і Node.js. Зрозуміло, область застосовності Erlang не обмежується Інтернетом, всякими там XMPP-серверами або, наприклад, розподіленими СУБД (спробуйте, до речі, застосувати тут Python!). При сильному бажанні на Erlang можна писати GUI або навіть під Android.
А що ви думаєте про Erlang?