Защо да учим и използваме Elixir?
Това е блог пост към първата лекция1 от курса Функционално програмиране с Elixir, провеждан във ФМИ през летния семестър на 2018/2019 година. В него ще отговорим на въпроса защо бихте учили и използвали Elixir. Но първо малко предистория.
Erlang
Erlang е функционален език, разработен в средата на 80-те години от Ериксон, с цел писане на телеком програми. Въпреки че мотивацията за създаването му е водена от спецификата на сферата на телекомуникациите, Erlang не се използва само в тази област. Eдна от по-рядко споменаваните причини2 за създаването му е желанието да се заменят използваните дотогава специализиран хардуер и софтуер (включително и операционна система). Erlang няма експлицитна поддръжка за програмиране на телефони или каквото и да е било друго телеком оборудване. Няма и изисквания за специален хардуер и операционна система. Вместо това езикът предлага решения за техническите изисквания и проблеми, срещани при създаването на подобен тип софтуер:
- конкурентност;
- скалируемост;
- дистрибутираност;
- толерантност към грешки;
- обновления без спиране на програмата;
- бързо отговаряне на всички заявки дори при високо натоварване;
Всичко това се случва във време, когато интернет не е широко разпространен и повечето програми работят на един компютър и без връзка с интернет. При този вид софтуер няма изисквания за дистрибутираност и хоризонтална скалируемост. По това време персоналните компютри не са разполагали и с 2-, 4- или 16-ядрени процесори - реално изискване софтуерът автоматично да започне да работи по-бързо при добавяне на още процесори/ядра не е съществувало. Директни следствия от това са дизайнът и предназначението на останалите програмни езици, разработени по това време или по-рано, както и езиците, базирани на тях.
Дори никога да не използвате Erlang, то идеите и подходът3 му към решаване на определен тип проблеми са напълно приложими при използването на други езици.
Къде е мястото на Erlang днес?
Добре, но колко от нас пишат софтуер за телекоми или софтуер с подобни изисквания? Всъщност доста от нас. Изискванията към съвременните уеб приложения се припокриват напълно с изискванията, с които са се съобразили създателите на Erlang. Голямо предимство е да имаш език и платформа, които са изградени на база задоволяване на тези изисквания, без нуждата от външни библиотеки, които да имплементират липсващата функционалност в езика и които заобикалят неговите недостатъци.
Ако искате да разберете повече за гаранциите, които ни дава BEAM4 - виртуалната машина, върху която се изпълнява Erlang, - то най-бързият начин е да изгледате това видео.
Elixir
Защо говорим толкова за Erlang в статия, която носи името “Защо да учим и използваме Elixir?”
Elixir е функционален език. Разработката му започва през 2011 г., а версия 1.0 се появява през септември 2014 г.
Elixir се изпълнява върху BEAM. Той може да използва всичко вече написано, както и всичко, което ще бъде написано на Erlang. Това ни дава възможността да съчетаваме нов и модерен език с библиотеки, издържали теста на времето. Извикването на Erlang код от Elixir не носи допълнително забавяне и нужда от използване на криптичен синтаксис или преобразувания на типове. Нека сравним разликите на извикване на Elixir и Erlang функция от даден модул:
Elixir:
DateTime.utc_now()
Erlang:
:crypto.strong_rand_bytes(64)
Elixir модулите започват с главна буква, а Erlang модулите започват с :
и малка буква.
Нека да разгледаме една малка част от нещата, заради които ние харесваме Elixir/Erlang:
- Функционален език - непроменими (immutable) и персистентни (persistent) структури от данни, съпоставяне на образци (pattern matching), функции от по-висок ред, композиция на функции.
- Баланс между чисти (pure) и функции със странични ефекти. Тук някои от заклетите фенове на Haskell може да не се съгласят. Но някои неща трябва да бъдат пожертвани на олтара на продуктивността.
- Процеси на ниво език - може би звучи скучно, но това е основата, на която се изгражда толкова известната конкурентност и дистрибутираност на Erlang/Elixir.
- OTP (Open Telecom Platfom) - това е платформа, която вече не се използва само за писане на телеком програми, а на софтуер, който има същите изисквания. Както споменахме по-горе, такива всъщност са доста от приложенията в ерата на интернет. По време на курса ще разгледаме част от библиотеките, които OTP предоставя. Засега повече информация може да намерите тук.
Грешно е да смятаме Elixir за конкурент, който ще убие Erlang. Напротив, Elixir е едно от най-хубавите неща, които можеше да му се случат. Erlang, който е неговата солидна основа, търпи все по-бурно развитие в следствие на интереса към Elixir и прииждането на нови хора в общността.
Но за да се появи Elixir и да правим курс за него, а не за Erlang, то той трябва да поставя нещо ново на масата.
- Elixir е Erlang. Всичко, което е плюс на Erlang, е плюс и на Elixir.
- Но Elixir е и нещо повече.
- По-добри инструменти в сравнение с Erlang.
- Хубава документация. Създателят на езика твърди, че грешки в документацията трябва да се третират като грешки в самата програма.
- Зад езика и неговата екосистема стоят опитни, отзивчиви и интелигентни хора.
- Удобен и красив синтаксис. Erlang има минималистичен синтаксис, дори прекалено. Това води до доста boilerplate и повторения в кода. Липсата на макроси подчертава този проблем.
- Мощни макроси. За разлика от тези в С/С++, макросите в Elixir не работят върху низове, ами върху AST (Abstract Syntax Tree). Макросите в Elixir са повече повлияни от тези в Lisp и Clojure.
- Полиморфизъм чрез протоколи.
- Качествени библиотеки. Доста библиотеки са написани на Erlang и са използвани дълго време, за да твърдим, че са стабилни и добре тествани. Но също така има доста библиотеки, написани сравнително наскоро на Elixir. Това има предимството много от грешките, направени в по-стари библиотеки в други езици да бъдат избегнати. Примери за такива нови библиотеки са: Plug, Ecto, Phoenix, GenStage и много други.
- Pipe оператор
|>
. С негова помощ кодът е безспорно по-красив, удобен, четим, лесен за писане и за промяна. Едно скрито предимство на това е консистентността, която той вкарва в стандартната библиотека на езика. За да e по-лесно използването на|>
, е прието функциите да приемат като първи аргумент данните, върху които работят.
Нека разгледаме един пример за разликата между код, който не използва |>
и код, който използва |>
.
Целта е да извършим поредица от трансформации върху списък от числа.
Един начин да напишем това е:
data = [1,2,3,4,5]
data_squared = Enum.map(data, fn n -> n*n end)
data_filtered = Enum.filter(data_squared, fn n -> n >10 end)
sum = Enum.reduce(data_filtered, 1, &(&1+&2))
Ами ако не искаме да използваме толкова много временни променливи? Можем да вложим функциите:
Enum.reduce(Enum.filter(Enum.map([1,2,3,4,5], fn n -> n*n end),fn n ->n > 10 end), 1, &(&1 + &2))
В този случай четимостта клони към нула, а добавянето на нова трансформация не е никак лека задача.
Тук идва на помощ |>
. Той подава резултата от лявата си страна като първи аргумент на израза в дясната си страна. Нищо повече, нищо по-малко, но достатъчно, за да можем да напишем нашия код по следния начин:
result =
[1, 2, 3, 4, 5]
|> Enum.map(fn n -> n * n end)
|> Enum.filter(fn n -> n > 10 end)
|> Enum.reduce(1, &(&1 + &2))
Ами лошите страни на Erlang/Elixir?
За да не се изреждат само суперлативи за езика, е редно да споменем и някои от не толкова хубавите му страни. Някои от тях са в този списък само защото изглеждат странно на пръв поглед, но всъщност са полезни и с течение на времето ще разберем защо.
- Липсата на библиотеки, налични в много останали езици. Тъй като Elixir е нов език, а Erlang няма популярността на Java/Python/Ruby, то понякога ще се случва да няма библиотеката, която очаквате. Понякога ще се случва да има единствено Erlang библиотека, която без проблеми може да използвате, но ще трябва да научите и малко Erlang, ако искате да четете кода ѝ.
- Заблудата, породена от изказвания на хора, незапознати с езика. Често срещано оплакване е, че синтаксисът около конкурентността не е толкова прост и минимален, както в Go. Това е така, защото конкурентността в Elixir решава проблеми, свързани с high availability, докато в Go се използва чисто и просто за конкурентността.
- На пръв поглед не е очевидно къде се намират някои функции. Ако искате да намерите дължината на списък, то функцията в модула
List
ли се намира? Всъщност откривате, че няма нитоList.size
, нитоList.length
. Дължината се намира сEnum.count
, която вътрешно използва:erlang.length
(достъпна и катоKernel.length
или простоlength
). Но сEnum.count
можем да намерим и броя елементи на всичко, което имплементира протоколаEnumerable
.
Проблемът с библиотеките е проблем на всеки език на този етап от развитието си. Нека уточним: тук не говорим за важни библиотеки - HTTP сървър/клиент, библиотеки за работа с бази данни, уеб фреймуърк, JSON декодери; такива библиотеки има, и то доста добри. Става въпрос за малките неща. Това не е проблем на език или платформата и е поправим, макар и бавно. На курса по Elixir във ФМИ ще даваме бонус точки за принос към някоя библиотека с отворен код. Още по-добре, ако създадете нова.
Източници:
[1] Цветинов, Николай (Meddle). Функционално програмиране с Elixir. Gitpich, 18.02.2019. Available from: https://gitpitch.com/ElixirCourse/presentations-2019/?p=welcome [cited 22.02.2019].
[2] Williams, Mike. The True story about why we invented Erlang and A few things you don’t want to tell your Manager. Erlang Factory, 26.02.2011. Available from: https://www.erlang-factory.com/upload/presentations/416/MikeWilliams.pdf [cited 22.02.2019].
[3] Hebert, Fred. The zen of Erlang. Ferd, 08.02.2016. Available from: https://ferd.ca/the-zen-of-erlang.html [cited 22.02.2019].
[4] Virding, Robert. Hitchhiker’s Tour of the BEAM. Erlang Factory, 2012. Available from: http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf [cited 22.02.2019].
Jurić, Saša. ElixirDaze 2017- Solid Ground by Saša Juric. YouTube, 16.03.2017. Availalble from: https://www.youtube.com/watch?v=5SbWapbXhKo [cited 22.02.2019].