Erlang
Erlang (Ерла́нґ /ˈɜːrlæŋ/ UR-lang) — мова функційного програмування з динамічною типізацією, призначена для розробки програм для різного роду розподілених і багатониткових систем. Розроблена і підтримується компанією Ericsson. Мова містить в собі засоби породження паралельних процесів та їхньої взаємодії за допомогою посилання асинхронних повідомлень, без використання блокувань. Мова орієнтована на розробку розподілених відмовостійких застосунків, які забезпечують паралельну обробку запитів в режимі реального часу. Мова набула поширення в таких областях, як телекомунікації, банківські системи, електронна комерція, комп'ютерна телефонія і організація миттєвого обміну повідомленнями. Програма транслюється в байт-код, який виконується віртуальною машиною, що забезпечує переносність. Одночасно розробниками випускається OTP (Open Telecom Platform) — супутній набір бібліотек і компонентів для розробки розподілених систем на мові Erlang. Код проєкту поширюється під модифікованою вільною ліцензією MPL. Свій синтаксис і деякі концепції, мова Erlang успадкувала від мови логічного програмування Пролог. Мова підтримує зіставляння зі взірцем, обробку виключень, спискові та бінарні генератори, анонімні функції, функції вищого порядку, обмін повідомленнями між процесами. Препроцесор підтримує роботу з макросами і включення заголовкових файлів. Популярність мови Erlang зросла у зв'язку з розширенням області використання (телекомунікаційні системи) на паралельні розподілені системи з високим навантаженням, які обслуговують мільйони користувачів WWW, такі як чати, системи керування веб-контентом, веб-сервери і розподілені бази даних, що вимагають масштабування. Erlang використовуються в деяких NoSQL-базах даних високої доступності[2]. Назва та історіяСеред малообізнаних з історією мови Erlang людей поширена думка, що назва «Erlang» розшифровується як ERicsson LANGuage. Насправді мова отримала свою назву на честь Агнера Крарупа Ерланга[en], данського математика, який працював у галузі телекомунікацій. Існує одиниця вимірювання телекомунікаційного трафіку, яка також називається Erlang[en].[3] На створення Erlang вплинули ML, Miranda[en], Ada, Модула-2[en], CHILL, Пролог. Окрім того, на спосіб оновлення програмного забезпечення вплинули Smalltalk і пропрієтарні мови EriPascal та PLEX, якими послуговувались в Ericson[4]. Мова Erlang була створена в 1986 році в лабораторії компанії Ericsson спільно Джо Армстронгом (Joe Armstrong), Робертом Вірдінгом (Robert Virding) і Майком Вільямсом (Mike Williams), і в 1998 році переведена в розряд відкритих проєктів. Завдяки початковій орієнтації на створення застосунків для паралельної обробки запитів в режимі реального часу мова набула поширення в таких областях як телекомунікації, банківські системи, електронна комерція, комп'ютерна телефонія і організація миттєвого обміну повідомленнями[5]. У 1998 році топ-менеджмент Ericsson вирішив не брати на себе зобов'язань з розробки та підтримки власної мови програмування, натомість зосередившись на Java. Використання Erlang було заборонено у нових проєктах Ericsson Radio AB у зв'язку з реалізацією плану аутсорсингу програмної технології компанії Rational Inc.[6] Це рішення дуже сильно вплинуло на майбутнє Erlang: воно призвело до відкриття коду Erlang під відкритою ліцензією EPL (аналог Mozilla Public License), а також послужило головною причиною початку поширення мови за межами компанії, що його створила. Основним запереченням проти відкриття вихідного коду було вирішення питань щодо патентів, але ці труднощі були подолані. Незабаром багато хто з основних розробників покинув Ericsson, щоб організувати власний бізнес — Bluetail AB.[4] На початку 2000-х років наукові кола стали виявляти інтерес до Erlang. З 2002 року став проводитись щорічний Erlang Workshop. Ericsson продовжував спонсорувати проєкт HiPE (від англ. High-Performance Erlang — високопродуктивний Erlang). Проєкт HiPE займався ефективною реалізацією мови та інструментами для перевірки типів, і створений у групі проєкту компілятор в машинний код, який входить у постачання версії Erlang/OTP, вільно розповсюджується з 2001 року. Роботи, пов'язані з Erlang, ведуть інші заклади вищої освіти. Інструменти для рефакторингу створені в Кентському університеті у Великій Британії та університеті Лоранда Етвеша в Угорщині, інструменти для різних видів тестування — у Мадридському політехнічному університеті, технічному університеті Чалмерса та Гетеборгському університеті. Коли системи з симетричною багатопроцесорністю тільки починали завойовувати ринок серверів і настільних комп'ютерів, кидаючи виклик розробникам програмного забезпечення, вже в 2006 перша версія Erlang з підтримкою SMP була випущена спільними зусиллями команди OTP з Ericsson і команди HiPE. Незабаром після цього вийшла перша майже за десятиліття велика монографія з Erlang: «Programming Erlang» Джо Армстронга, після чого багато розробників «відкрили» для себе Erlang/OTP, і мова стала набирати популярності.[7] Процес розвитку мови включає в себе розгляд пропозицій з розвитку EEP (Erlang Enhancement Proposal). За допомогою цих пропозицій Erlang-спільнота вносить зміни у стандартну подачу Erlang. З внесеними пропозиціями можна ознайомитися на веб-сторінці erlang.org/eeps. ФілософіяЗа свідченнями Майка Вільямса, Erlang створювався для вирішення трьох проблем розробки розподілених систем м'якого реального часу з високим ступенем паралелізму:
За словами Вільямса, філософія, якої дотримувались розробники Erlang, пасує і для написання програмного забезпечення цією мовою:[8]
Більшість мов, створених раніше за Erlang, були розроблені без попереднього знаходження застосування, тоді як Erlang був розроблений спеціально на основі вимог до розподілених, стійких до відмов, паралельних систем реального часу. З розвитком Інтернету виявилося, що багато програм мають аналогічні вимоги, що пояснює зростання інтересу до мови.[9] Секрет високої стійкості до відмов полягає у використанні ізольованих один від одного легких процесів, пов'язаних лише механізмом обміну повідомленнями та сигналами виходу. Принципи розробників на Erlang стосовно обробки помилкових ситуацій у процесах, можна виразити у вигляді висловлювання: «Let it crash»[10]. Пов'язано це з тим, що в Erlang-системі легко стежити за завершенням процесу, завершувати процеси, пов'язані зі збоями, і запускати нові процеси.[11] ОсобливостіСинтаксис успадкований від мови Prolog. Підтримує модулі, поліморфні функції, зіставлення за шаблоном, анонімні функції, умовні конструкції, структури, обробку винятків, оптимізацію хвостової рекурсії. Головна риса Erlang — модель легких процесів. Процеси є дешевими, їхнє створення вимагає таку кількість ресурсів, що їх можна порівняти з викликом функції. Єдиним способом взаємодії процесів є асинхронний обмін повідомленнями. Процес може встановити зв'язок (link) з іншими процесами і за вибором або отримувати повідомлення про їхнє дострокове завершення з вказанням причини або розділити їхню долю. Процес має свою «поштову скриню», звідки може вибірково читати повідомлення. Мова програмування Erlang сприяє створенню великої кількості конкурентних процесів. Процеси ізольовані та не мають спільного стану. Процес проєктування полягає в ітеративному розбитті системи на ієрархії підсистем, які конкурентно взаємодіють, доки складові не стануть достатньо простими для реалізації. Сув'язь «Процеси + повідомлення» на відміну від сув'язі «Об'єкти + Інтерфейси + Успадкування» часто дає компактніші рішення. Відсутність потреби блокування доступу до стану процесу для синхронізації їхньої взаємодії суттєво спрощує програмування. Для конкурентного ресурсу зазвичай створюється процес-монітор, через який здійснюється взаємодія з ресурсом. Також важливий момент полягає у формулі «let it crash» («нехай процес впаде»). Замість перехоплювати помилки і намагатися продовжувати роботу, частина програми, що містить ризикований код, відокремлюється у незалежний процес-камікадзе, який система вбиває у випадку виникнення помилки, при цьому батьківський процес отримує повідомлення про смерть нащадків і, за потреби, може перезапускати ці процеси. Такий підхід позбавляє код численних перевірок. Високорівневі конструкціїErlang є декларативною мовою програмування, яка використовується більше для опису того, що має бути обчислено, ніж як. Наприклад, визначення функції, яке використовує зіставлення зі зразком для вибору одного з варіантів обчислення або вилучення елемента даних зі складової структури нагадує рівняння. Зіставлення зі взірцем поширене навіть на бітові рядки, що спрощує реалізацію телекомунікаційних протоколів.[8] Функції є об'єктами першого класу Erlang. У мові також широко застосовуються характерні для функціональної парадигми програмування спискові включення (генератори списків).[8] Паралельні обчисленняОсобливістю мови Erlang є застосування легковагих процесів відповідно до моделі акторів. Такий підхід дозволяє виконувати одночасно сотні тисяч і навіть мільйони таких процесів, кожен з яких може мати скромні вимоги до пам'яті. Процеси ізольовані один від одного і не мають загального стану, але між ними можна встановити зв'язок та отримувати повідомлення про їхній стан.[8] Для взаємодії процесів використовують асинхронний обмін повідомленнями. Кожен процес має свою чергу повідомлень, обробка якої використовує зіставлення із взірцем. Процес, що надіслав повідомлення, не отримує повідомлення про доставку, навіть якщо ідентифікатор процесу одержувача недійсний або одержувач ігнорує повідомлення. Таким чином, відповідальність за правильно організовану взаємодію між процесами лежить на розробникові.[9] Наприклад, при реалізації на Erlang мережевого чату, структура програми може безпосередньо відображати одночасність дій користувачів щодо обміну повідомленнями шляхом запуску нових процесів. Ефективність передачі повідомлень зберігається зі збільшенням кількості процесів, а вимоги до пам'яті мінімізуються за рахунок того, що легковагими процесами керує віртуальна машина, а не засоби операційної системи.[8] Розподілені обчисленняErlang з самого початку проєктувався для розподілених обчислень та масштабованості. Підтримка паралелізму вбудована в синтаксис та семантику мови, тому побудову системи можна вести, абстрагуючись від конкретного місця обчислень. У стандартній поставці Erlang може налагодити зв'язок процесів протоколу TCP/IP незалежно від підтримуваних ним платформ (операційних систем).[8] Запущений екземпляр емулятора Erlang називається вузлом (node). Вузол має ім'я і «знає» про існування інших вузлів на цій машині або у мережі. Створення та взаємодія процесів різних вузлів не відрізняється від взаємодії процесів всередині вузла. Програми, написані на Erlang, здатні працювати на кількох вузлах. Вузлами можуть бути процесори, багато ядер одного процесора, і навіть цілий кластер машин. Для створення процесу на іншому вузлі процесу достатньо знати його ім'я і, без особливих підстав, він може не цікавитися фізичним розташуванням процесу, що з ним взаємодіє. Синтаксис відправки повідомлення процесу на своєму вузлі та віддаленому однаковий.[8] Завдяки вбудованим у мову можливостям розподілених обчислень об'єднання в кластер, балансування навантаження, додавання вузлів та серверів, підвищення надійності викликають лише невеликі витрати на додатковий код. За замовчуванням, вузли спроєктовані для роботи всередині відокремленого сегмента мережі (DMZ), але, якщо необхідно, комунікація між вузлами може відбуватися із застосуванням протоколу SSL, захищеного криптографічними методами.[8] М'який реальний часПрограми високорівневою мовою Erlang можуть бути використані в системах м'якого реального часу (який іноді перекладають як «псевдореальний» або «квазіреальний»). Автоматизоване керування пам'яттю та збирання сміття діють у рамках одного процесу, що дає можливість створювати системи з мілісекундним часом відгуку (навіть незважаючи на необхідність збирання сміття), що не відчувають погіршення пропускної здатності при високому навантаженні.[8] Гаряча заміна кодуДля систем, які не можуть бути зупинені для оновлення коду, Erlang пропонує гарячу заміну коду (англ. hot code upgrade). При цьому в додатку можуть одночасно працювати старі і нові версії коду. У такий спосіб програмне забезпечення на Erlang може бути модернізовано без простоїв, а виявлені помилки виправлені.[12] Опис мовиТипи данихТипізація в Erlang є сильною та динамічною. Динамічна типізація була обрана для мови Erlang через те, що перші розробники були більше з нею знайомі.[13] На думку Джо Армстронга, статична типізація вимагала б дуже великих трудовитрат, зокрема, реалізувати систему гарячого дозавантаження коду було б дуже важко. Така типізація, коли можливі помилки типів виявляються лише під час виконання, не завадила можливості створювати системи з дуже високим рівнем доступності. Дані Erlang є незмінними: операції не переписують старі значення, що знаходяться в пам'яті. Якщо необхідно, модулі на Erlang можна забезпечити описами та визначеннями нових типів (що не впливають на компіляцію програми) для автоматичної перевірки типів за допомогою утиліти Dialyzer.[13] Числа
У Erlang є два типи числових літералів: цілі і з рухомою комою, наприклад: Erlang/OTP 23 [erts-11.2.2.4] [source] [64-bit] [smp:20:20] [ds:20:20:10] [async-threads:1] [hipe]
Eshell V11.2.2.4 (abort with ^G)
1> 123 / 23 + 12 * (2 + 3).
65.34782608695652
2> math:cos(math:pi()).
-1.0
3> random:uniform(10).
5
АтомиАтом — константа з ім'ям, що має бути взята в одинарні лапки, якщо не починається з малої літери або містить знаки, окрім літер, цифр, підкреслення, крапки та символу Бітові рядки та бінарні даніБітовий рядок використовується для зберігання пам'яті нетипізованих даних. Рядки, що складаються з цілої кількості октетів, називаються бінарними (або двійковими) даними (англ. binaries). Синтаксис опису бітового рядка досить гнучкий, оскільки визначає значення бітів окремих діапазонів і може бути забезпечений модифікатором типу.[8] Декілька прикладів в інтерактивній командній оболонці: 1> <<23,89,120>>.
<<23,89,120>>
2> <<"ABC">>.
<<65,66,67>>
3> <<10,17,42:16>>.
<<10,17,0,42>>
4> <<$a, $b, $c>>.
<<"abc">>
5> <<1024/utf8>>.
<<208,128>>
Конструктори бітових рядків (англ. bitstring comprehension) аналогічні списковим генераторам, але працюють над бітовими рядками: 1> << <<bnot(X):1>> || <<X:1>> <= <<2#111011:6>> >>.
<<4:6>> У цьому прикладі змінна X послідовно отримує біти числа КортежКортеж (англ. tuple) — складений тип даних з фіксованою кількістю елементів. При доступі до елементів кортежу за допомогою вбудованих функцій, нумерація елементів починається з одиниці, а не з нуля. Перший елемент кортежу (атом) можуть використовувати для опису ролі кортежу у програмі — його називають тегом (англ. tag — «мітка»). В Erlang прийнято будувати різні типи даних на основі кортежів з тегами, це полегшує зневадження програми та вважається гарним стилем програмування.[8] Для роботи з кортежами є декілька вбудованих функцій, наприклад:[8] 1> tuple_size({a, 1, "777"}).
3
2> element(1, {b, 2, 3, 4}).
b
3> setelement(1, {c, 5}, d).
{d,5}
СписокСписок (англ. list) — складений тип даних зі змінною кількістю елементів. Для маніпуляції зі списками можна використовувати функції модуля Списки можна записувати і більш звичним способом. Наступні записи еквівалентні: 1> [a|[b|[c|[]]]].
[a,b,c] Для роботи зі списками можна використовувати конструктори списків[13](генератори списків — en: List Comprehensions), наприклад: 1> [X / 2 || X <- [1,2,3,4]].
[0.5,1.0,1.5,2.0]
Модуль 1> lists:split(2, [l,2,3] ++ [4,5,6]).
{[l,2],[3,4,5,6]}
А це приклад того, що рядок виду «test» в Erlang є списком кодів символів: 1> "test" =:= [$t, $e, $s, $t].
true
2> "test" =:= [116, 101, 115, 116].
true
3> $t.
116
У модулі
third_degree(R) ->
lists:map(fun(X) -> math:pow(X, 3) end, R).
В даному прикладі функція math: pow(X, 3) підносить до степеня 3 кожен елемент списку R.
multiple_three(List) ->
lists:filter(fun(X) -> X rem 3 =:= 0 end, List).
В даному прикладі з вхідного списку List у новий — вихідний список попадуть лише елементи, кратні трьом.
Функція згортання приймає два аргументи: поточний елемент списку та поточне значення акумулятора. І повертає нове значення акумулятора. Класичні приклади — обчислення суми та добутку елементів масиву: 1> Numberlist = [11, 12, 13, 14, 15].
[11, 12, 13, 14, 15]
2> lists:foldl(fun(Item, Acc) -> Acc + Item end, 0, Numberlist).
65
3> lists:foldl(fun(Item, Acc) -> Acc * Item end, 1, Numberlist).
360360
Наступний приклад ілюструє роботу функції правої згортки lists: foldr (англ. fold — згорнути, «r» від англ. right — правий), першим параметром якої має бути функція: 1> D = fun(V, A) -> V / A end. % функція D - ділення V на A
#Fun<erl_eval.12.82930912>
2> lists:foldr(D, 1, [1, 2, 4, 8]).
0.25
3> 1/(2/(4/(8/1))).
0.25
Результат виконання згортки справа наліво (у рядку 2) тотожний ланцюжковому поділу (рядок 3). Другий параметр Зазвичай користуються foldl — тому, що операція отримання елементу-голови списку виконується за константний час, а для отримання елементу з кінця списку потрібно пройтись через весь список[16]. РядокУ Erlang немає самостійного типу для рядків — внутрішньо рядки є списками кодів символів. Рядок Бінарні послідовності записуються та відображаються, як послідовності цілих чисел або рядків, поміщені між подвійними символами 1> <<5,10,20>>.
<<5,10,20>>
2> <<"hello">>.
<<"hello">>
Бінарна послідовність, вироблена за допомогою term_to_binary, зберігається в так званому зовнішньому форматі терму. Терми, які були конвертовані в бінарні послідовності з використанням term_to_binary, можуть бути збережені в файли, передані в повідомленнях по мережі тощо, а початковий терм, з якого вони були зроблені, може бути відновлений пізніше. Це надзвичайно корисно для збереження складних структур даних у файли або надсилання складних структур даних на віддалені машини.[16] 1> B = term_to_binary("useful").
<<131,107,0,6,117,115,101,102,117,108>>
2> binary_to_term(B).
"useful"
Атоми і рядки зовні досить схожі, але мають різні реалізації. Тоді як атоми можна лише порівнювати, рядки підтримують багато інших операцій, їм є безліч функцій в модулях Логічні значенняДля значень булевої логіки істинне та хибне в Erlang застосовуються атоми 1> 2 < 3.
true
2> is_boolean(125).
false
Функціональний об'єкт (Fun)
1> lists:map(fun(X) -> X + 1 end, [1, 2, 3]).
[2,3,4]
2> Belongs = fun lists:member/2.
#Fun<lists.member.2>
3> Belongs(a, [a, b]).
true
ЗаписЩоб позначати окремі елементи кортежів (полегшити роботу з великими кортежами) і уникнути помилок при написанні програми, в Erlang було додано синтаксичний цукор — синтаксис записів (англ. record). Записів не існує в час виконання — на етапі компіляції записи перетворюються (транслюються) в кортежі — ключі губляться, відповідно, в час виконання існують лише значення записів. Для роботи з записами, необхідно спочатку дати опис запису директивою -record(user, {login = "anonymous", email, password}).
З цього опису компілятор дізнається, що маються на увазі кортежі з чотирьох елементів, в яких елементи з другого по четвертий відповідають полям Створення записів та вилучення елементів запису завжди вимагає явної вказівки імені запису:[17] R0 = #user{}, % всі поля отримують значення за замовчуванням
R1 = #user{login = "user1", email = "user@mail.com", password = "secret"}, % Задаємо значення для всіх полів
Оновлення одного значення в наявному записі: оновлення двох значень в наявному записі: отримати значення по ключу з запису КартаАсоціативний масив (словник) (en: map) зберігає пари виду «(ключ, значення)». І ключем, і значенням може бути будь-який терм Erlang.[18] Map = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "hi" => 42},
Key = "hi",
maps:find(Key,Map).
{ok,42}
Інші типиУ мові Erlang є інші типи даних. Тип посилання (англ. reference) є унікальним у межах вузла/кластера з'єднаних вузлів в середовищі часу виконання Erlang. Посилання створюється викликом функції Ідентифікатор порту (англ. port identifier) визначає порт для зв'язку із зовнішнім по відношенню до Erlang-системи світом. Порт дозволяє процесу, що його створив-власнику (так званому приєднаному процесу) обмінюватися бінарними повідомленнями зі сторонніми програмами і ОС способом, прийнятим в даній операційній системі.[17][19] Ідентифікатор процесу (англ. Pid), як і випливає з його назви, ідентифікує процес, що породжується різними функціями Вбудовані функції для роботи з типамиДля перетворення типів використовуються вбудовані функції (BIF,англ. builtin function) виду x_to_y («з x в y»), а для перевірки належності значення того чи іншого типу — функції виду is_x («є_x»): 1> atom_to_list(hello).
"hello"
2> list_to_binary("world").
<<"world">>
3> tuple_to_list({1,2,3,4}).
[1,2,3,4]
1> is_integer(3).
true
2> is_tuple("abc").
false
3> is_integer(3.0).
false
ОпераціїАрифметичні операціїErlang надає найбільш поширені арифметичні операції для цілих чисел і чисел з плаваючою комою:
Всі ці операції лівоасоціативні. Унарні операції мають найвищий пріоритет, потім слідує множення і ділення, найменший пріоритет у складання та віднімання. При необхідності ціле може приводитися до типу з рухомою комою.[8] Бітові операціїБітові операції працюють над цілими числами і дають в результаті ціле число.[8]
Логічні операціїЛогічні операції працюють над логічними значеннями
Операції порівнянняОперації порівняння отримують два операнди, а результатом операції є логічне значення
1> 5 == 5.
true
2> 5 == 5.0.
true
4> 5 =:= 5.
true
5> 5 =:= 5.0.
false
Можна порівнювати значення різних типів, але вони вважаються в Erlang впорядкованими наступним чином:[8] число < атом < посилання < функція < порт < ідентифікатор процесу < кортеж < список < бінарні дані Списки вважаються впорядкованими в лексикографічному порядку, а кортежі порівнюються за довжиною, і лише потім у лексикографічному порядку.[8] ЗмінніЗмінні служать для зберігання значень простих і складових типів. Ім'я змінної починається з великої літери (у спеціальних випадках — з підкреслення) і може містити букви, цифри, підкреслення. Змінній можна присвоїти значення лише один раз — ця властивість мови програмування називається одиничним присвоєнням (англ. single assignment). До переваг одиничного присвоєння можна віднести усунення необхідності в блокуваннях, а також спрощення налагодження програми[20] Відбувається обчислення(редукція) аргументів функції перед застосуванням функції до цих аргументів (вхідних параметрів). Область видимості змінної поширюється від її визначення (en: bind — зв'язування) до закінчення клози функції. Приклад: binomial(X) ->
Y = X * X,
X + Y.
prod([1|T]) -> prod(T);
prod([Y|T]) -> Y * prod(T);
prod([]) -> 1.
У цьому прикладі область видимості При виході обчислень за межі області видимості змінної, пам'ять, зайнята її вмістом, може бути звільнена у процесі збирання сміття, якщо значення змінної не використовується в іншій частині програми.[17] Зіставлення зі зразком (en: Pattern Matching) використовується в Erlang для присвоєння (у тому числі, при роботі з параметрами функцій), управління потоком виконання програми, отримання значень складових типів, вибору повідомлення з черги. У лівій частині порівняння (або в заголовку функції) можуть бути пов'язані (вже мають значення) і незв'язані (отримуючі значення) змінні, а також літерали (атоми, числа, рядки). В результаті виконання порівняння може виявитися успішним (у цьому випадку змінні зв'язуються зі значеннями) та неуспішним — змінні залишаються непов'язаними. У зразку можуть бути змінні, значення яких для зразка байдуже: їх імена записуються з підкреслення. Змінні, ім'я яких починається з підкреслення, це звичайні змінні, крім того, що компілятор не буде скаржитися, якщо вони не використані. Не можна прив'язувати до них значення більше одного разу.[8] ФункціїПрограми Erlang складаються з функцій, які викликають одне одного. Кількість вхідних аргументів (параметрів) функції називається арністю. При виклику функції заголовки функції зіставляються зі зразком. У разі збігу параметрів виклику формальні параметри зв'язуються з фактичними і виконується відповідна частина тіла функції. Запис варіанту обчислення функції для деякого зразка може називатися клозом від англ. clause, а визначення функції — це набір з одного або більше клозів.
Для уточнення зіставлення зі зразком у функціях можна використовувати охоронні вирази, які випливають після ключового слова sign(X) when X > 0 -> 1;
sign(X) when X == 0 -> 0;
sign(X) when X < 0 -> -1.
Erlang перебирає (матчить) клози функції в тому порядку, в якому вони записані (зверху вниз, зліва направо), доки не буде знайдено відповідний вираз (заголовок клози), який зматчиться.[9] В охоронних виразах можна використовувати лише обмежений набір вбудованих функцій, оскільки ці функції не повинні мати побічних ефектів. Функції Erlang підтримують рекурсивні виклики. У разі коли визначення функції закінчується рекурсивним викликом (хвостова рекурсія), Erlang використовує оптимізацію: стек викликів не застосовується.[9] Як параметром, і результатом функції може бути інша функція. У наступному прикладі функція одного аргументу повертає функцію додавання аргументу[8] 1> Plus = fun(X) -> fun(Y) -> X + Y end end. % Визначення функції, яка повертає функцію
#Fun<erl_eval.6.82930912>
2> Plus(2). % Функція повертає Fun-об'єкт
#Fun<erl_eval.6.82930912>
3> Plus(2)(3). % Такий синтаксис не працює
* 1 : syntax error before : '('
4> ( Plus(2) )(3). % Додаткові дужки дозволяють досягти необхідного результату
5
5> Plus2 = Plus(2), Plus2(3). % Те саме з використанням додаткової змінної
5
Умовні виразиОкрім матчингу та охоронних виразів у клозах функції, в Erlang є інші умовні вирази вибору: if та case. Вираз вибору дозволяє організувати зіставлення зі зразком усередині функції і зазвичай має наступний синтаксис: if
охорона1 -> вираз11, вираз12, ... ;
охорона2 -> вираз21, вираз22, ... ;
...
охоронаN -> виразN1, виразN2, ...
end
Тут if
X =< 0 -> io:format("менше або дорівнює нулю ~n", []), <<"менше або дорівнює нулю"/utf8>>;
X > 0, X < 10 -> io:format("більше нуля та менше десяти ~n", []), <<"більше нуля та менше десяти"/utf8>>;
X == 10; X == 11 -> io:format("дорівнює десяти або одинадцяти~n", []), <<"дорівнює десяти або одинадцяти"/utf8>>;
true -> io:format("більше або дорівнює дванадцяти~n", []), <<"більше або дорівнює дванадцяти"/utf8>>
end.
Якщо жодна захисна послідовність не є істинною, виникає помилка виконання -module(badexample).
-export([broken_if/1]).
broken_if(X) ->
if
X < 0 -> Z = -1;
X >= 0 -> Y = 1
end,
Y * Z.
При спробі скомпілювати модуль виникають повідомлення про помилки, тому що в такому коді змінні не пов'язуються зі значенням в іншій клозі відповідно: 1> c(badexample).
badexample.erl:8: variable 'Y' unsafe in 'if' (line 4)
badexample.erl:8: variable 'Z' unsafe in 'if' (line 4)
error
Правильним було б визначити всі використовувані далі за кодом змінні у всіх гілках if-вирази.[9] -module(if_else).
-export([compare/2, ascii/1, run/3]).
compare(X, Y) ->
Result = if
X > Y -> greater;
X =:= Y -> equal;
X < Y -> less
end,
io:format(" ~p is ~p than ~p ~n", [X, Result, Y]).
ascii(Letter) ->
Code = if
Letter =:= 'A' -> 101;
Letter =:= 'B' -> 102;
true -> unknown
end,
io:format(" ~p = ~p ~n", [Letter, Code]).
run(X, Y, Letter) ->
compare(X, Y),
ascii(Letter).
Можна використовувати вираз case expression of
pattern1 [when guard1] -> expr_seq1;
pattern2 [when guard2] -> expr_seq2;
...
end.
Спочатку обчислюється expression, припустимо, що при цьому воно набуває значення Value. Далі Value співставляється з pattern1 (разом з опціональним контролером guard1), pattern2 і так далі, до першого успішного співставлення. Як тільки це станеться, виконується послідовність виразів (expr_seqN) і результат цього обчислення стає результатом всього даного виразу. Якщо жоден із патернів не підійде, то відбувається виняткова ситуація.[16] -module(case_of).
-compile([export_all, nowarn_export_all]).
admit(Person) ->
case Person of
{male, Age} when Age > 21 -> yes_with_cover;
{female, Age} when Age > 21 -> yes_no_cover;
{male, _} -> no_boy_admission;
{female, _} -> no_girl_admission;
_ -> unknown
end.
run(Person) ->
Record = admit(Person),
io:format(" ~p~n", [Record]).
ПрикладиВ функціональних мовах програмування відсутні цикли, натомість використовується рекурсія. Розглянемо функції обчислення факторіалу: -module(fact).
-export([fac/1, func/1]).
fac(0) -> 1;
fac(N) when N > 0 ->
N * fac(N-1).
Це рекурсія без хвостової оптимізації, вона добре працює для невеликої глибини рекурсії. Проте, стає проблематичною у випадку великої глибини рекурсії. Чим більше операцій рекурсії — тим більше займе пам'яті дана програма у стеку при виконанні, дана рекурсія розгортається у стек — відповідно, чим більше рекурсивних викликів — тим більше пам'яті займе дана програма при виконанні.[16] Хвостова рекурсія — це рекурсія, яка викликається останньою інструкцією, таким чином стек залишається незмінним (або ж практично незмінним) і функція може працювати перманентно без зупинки.[16] func(N) -> func(N,1).
func(0,A) -> A;
func(N,A) when N > 0 -> func(N-1, N*A).
Хвостова рекурсія незалежна від кількості рекурсивних викликів — в пам'яті залишаються лише два числа. Спробуємо написати функцію обчислення N-ного числа Фібоначчі на Erlang двома шляхами-алгоритмами. Спочатку звичайна рекурсія. -module(fibonacci).
-export([fib_usual/1, fib_tail/1]).
fib_usual(1) -> 0;
fib_usual(2) -> 1;
fib_usual(N) when N > 0 ->
fib_usual(N - 1) + fib_usual(N - 2).
Дана функція тримає всі числа-проміжні результати одночасно у памяті, і кількість рекурсивних викликів функції — активних процесів — зростає надто стрімко. Хвостова рекурсія в даному випадку пишеться з використанням двох тимчасових змінних в якості параметрів нашої функції. Такі змінні називаються акумуляторами та використовуються для зберігання проміжних результатів наших обчислень, щоб не зберігати багато незакінчених обчислень (процесів, які очікують результат розрахунків від інших процесів) у пам'яті. fib_tail(N) when is_integer(N), N > 0 ->
fib_tail(N, 1, 0).
fib_tail(0, A1, _) -> A1;
fib_tail(N, A1, A2) ->
fib_tail(N - 1, A2, A1 + A2).
У памяті тримаються лише три числа та займається обчисленнями лише один процес. Додамо декілька функцій, які працюють рекурсивно зі списком і виконують такі звичні для всіх нас завдання: - визначення довжини масиву; - знаходження максимального (мінімального) елемента масиву; - обчислення суми всіх елементів масиву. 1. Визначення довжини масиву: - спочатку звичайна рекурсія len_list([]) -> 0;
len_list([_]) -> 1;
len_list([_H|T]) when is_list([_H|T]) ->
1 + len_list(T).
- хвостова рекурсія з акумулятором length_tail(List) when is_list(List)->
length_tail(List, 0).
length_tail([], Acc) -> Acc;
length_tail([_|Tail], Acc) ->
length_tail(Tail, 1 + Acc).
2. Знаходимо максимальний елемент масиву: - спочатку звичайна рекурсія max_el([Last]) when is_number(Last) -> Last;
max_el([H | T]) when hd(T) >= H ->
max_el(T);
max_el([H | T]) ->
max_el([H | tl(T)]).
- та сама функція, з допомогою хвостової рекурсії і акумулятора tail_maxx([H | T]) when is_number(H)->
tail_maxx(T, H).
tail_maxx([], Acc) when is_number(Acc) ->
Acc;
tail_maxx([H | T], Acc) when H >= Acc ->
tail_maxx(T, H);
tail_maxx([_ | T], Acc) ->
tail_maxx(T, Acc).
3. Підрахуємо суму елементів масиву: - звичайна рекурсія sumx([]) -> 0;
sumx([H | T]) -> H + sumx(T).
- підрахунок суми елементів масиву з допомогою хвостової рекурсії tail_sum(List) -> tail_sum(List, 0).
tail_sum([], Acc) -> Acc;
tail_sum([H | T], Acc) -> tail_sum(T, Acc + H).
Один з алгоритмів сортування — швидке сортування[12] -module(qsort).
-export([qsort/1]).
qsort([]) -> [];
qsort([Pivot|Rest]) ->
qsort([Front || Front <- Rest, Front < Pivot]) ++ [Pivot] ++
qsort([Back || Back <- Rest, Back >= Pivot]).
У цьому прикладі функція Препроцесор та макросиПрепроцесор Erlang (EPP) дозволяє вкладати файли з вихідним кодом один в інший, визначати макроси та здійснювати прості та параметризовані макропідстановки.[8] Макрос визначається за допомогою директиви -define(ZERO(X), X == 0).
is_zero(T) when ?ZERO(X) -> true;
is_zero(T) -> false.
Ім'я макроса зазвичай пишеться великими літерами. Визначення макроса має містити лексеми Erlang цілком (наприклад, спроба задати частину імені змінної за допомогою макроса викликає синтаксичну помилку). Макроси можуть використовуватися для підвищення зручності читання коду в охоронних виразах, для операторів налагодження і т. ін. Препроцесор має декілька визначених макросів, які не можна перевизначити: Заголовний файл (розширення Обробка помилок
Для обробки виняткових ситуацій Erlang можна застосовувати конструкцію try вираз для обчислення of
зразок1 when охорона1 -> вираз1;
зразок2 when охорона2 -> вираз2;
...
зразокN when охоронаN -> виразN
catch
класс1:зразокВикл1 when охоронаВикл1 -> виразВикл1;
...
классМ:зразокВиклМ:Stacktrace when охоронаВиклМ -> виразВиклМ;
end
Як і у випадку case-виразу, вираз, що обчислюється, зіставляється зі зразком (частини між 1> try math:sqrt(-1) catch error:Error -> {error, Error} end.
{error, badarith}
2> try math:sqrt(4) catch error:Error -> {error, Error} end.
2.0
Для створення винятків, визначених користувачем, використовується функція До розробки Річардом Карлссоном (Richard Carlsson) з команди проєкту HiPE описаного вище нового механізму обробки винятків (з'явився у версії R10B) в Erlang використовувалися catch-вирази.[8] МодуліКод програми на Erlang можна розбити на окремі модулі. Модуль — це ім'я для набору функцій, організованих в одному файлі. Ім'я модуля має збігатися з ім'ям файлу (якщо відкинути розширення)[8]. Модуль можна відкомпілювати в байт-код як із командного рядка операційної системи, так і з командної оболонки Erlang[13]. У файлі модуля можна записати оголошення функцій та директиви (іноді називаються атрибутами). Обов'язковим атрибутом є лише Функції в Erlang однозначно визначаються модулем, ім'ям та арністю. Наприклад, Вихідний текст модуля компілюється в BEAM-файл — файл, що містить байт-код віртуальної машини BEAM (англ. Bogdan’s/Björn's Erlang Abstract MachineBogdan's/Björn's Erlang Abstract Machine[23]). У свою чергу, ERTS (англ. Erlang Runtime SystemErlang Runtime System — система часу виконання Erlang) виконує цей код.[9] ПроцесиОсновною абстракцією паралельного програмування в Erlang є процес. Процеси можуть породжувати інші процеси, виконуватися одночасно, обмінюватися повідомленнями, реагувати на завершення один одного. Створення процесівErlang має невеликий, але потужний набір примітивів для створення процесів і спілкування між ними. Міжпроцесовий зв'язок працює за допомогою асинхронної системи передачі повідомлень, яка не має спільного доступу: кожен процес має «поштову скриньку» — чергу повідомлень, які були надіслані іншими процесами та ще не використані. Для отримання повідомлень, які відповідають бажаним паттернам, процес використовує примітив Для створення нового процесу служить декілька вбудованих функцій ( Наведений нижче приклад коду показує вбудовану підтримку розподілених процесів: %% Створимо процес та викличемо функцію web:start_server(Port, MaxConnections)
ServerProcess = spawn(web, start_server, [Port, MaxConnections]),
%% Створимо віддалений процес та викличемо функцію
%% web:start_server(Port, MaxConnections) на віддаленій ноді RemoteNode
RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),
%% Відправимо повідомлення процесу ServerProcess (асинхронно).
%% Повідомлення складається з кортежа з атомом "pause" та числом "10".
ServerProcess ! {pause, 10},
%% Отримаємо повідомлення, надіслані цьому процесу
receive
a_message -> do_something;
{data, DataContent} -> handle(DataContent);
{hello, Text} -> io:format("Got hello message: ~s", [Text]);
{goodbye, Text} -> io:format("Got goodbye message: ~s", [Text])
end.
Як показує приклад, процеси можуть бути створені у віддалених вузлах, і взаємодія з ними є прозорою у тому сенсі, що взаємодія з віддаленими процесами працює точно так само, як взаємодія з локальними процесами. Розподіленість (en: Concurrency) підтримує основний метод обробки помилок в Erlang. Коли процес аварійно завершується, він акуратно завершує роботу та надсилає повідомлення керуючому процесу, який потім може вжити заходів, наприклад запустити новий процес, який бере на себе завдання старого процесу. Передача та прийом повідомленьЯк і мова Occam, Erlang використовує для відправки повідомлення синтаксис зі знаком оклику: receive
зразок1 when охорона1 -> вираз11, вираз12, ...;
зразок2 when охорона2 -> вираз21, вираз22, ...;
...
зразокN when охоронаN -> виразN1, виразN2, ...;
Непов'язанаЗміннаДляІншихПовідомлень -> вираз1, вираз2, ...
end
Зустрівши такий вираз, інтерпретатор послідовно переглядає повідомлення з черги. Кожне повідомлення інтерпретатор зіставляє зі зразком і, якщо воно відповідає зразку, то обчислюються відповідні вирази. Коли всі повідомлення перебрані, і потрібного не виявилося, процес очікує нових повідомлень, після чого перебір черги повторюється. Якщо в receive-виразі відсутній зразок, якому задовольняє будь-яке повідомлення, такий вираз називається вибірковим receive-виразом.[8] Обробка помилок та завершення процесівПроцес можна зв'язати з іншим, у результаті між процесами встановлюється двонаправлене з'єднання (англ. link). Якщо один із процесів завершується ненормально, всім пов'язаним з ним процесам передається сигнал виходу (англ. exit signal). Процеси, що отримали сигнал, завершуються, поширюючи сигнал далі. Сигнал виходу є кортежем, елементами якого є атом Процес може здійснити перехоплення помилки (англ. trapping errors), якщо він має прапорець перехоплення виходу. Такий процес отримує сигнали виходу пов'язаних з ним процесів у вигляді звичайних повідомлень з аналогічною структурою кортежу. Перехоплений сигнал виходу більше не передається пов'язаним з процесом-перехоплювачем процесам. Сигнал виходу з причиною — атомом Erlang має можливість встановити і однонаправлене з'єднання. При завершенні процесу, що спостерігається, процес-спостерігач отримує повідомлення із зазначенням причини завершення. Процес може зупинити себе сам чи зупинити інший процес, викликавши функцію Ввід-вивідУ планувальнику процесів Erlang-системи проблема вводу-виводу, властива багатьом іншим мовам паралельного програмування, вирішена достатньо елегантно. Управління вводом-виводом, інтегроване з планувальником, вже на нижньому рівні здійснюється на основі подій, що дозволяє програмі обробляти вхідні та вихідні дані без зайвих блокувань. Такий підхід вимагає меншого числа випадків установлення та розриву з'єднань, а також прибирає необхідність блокування та перемикання контексту. Спосіб частіше застосовується в системах з явними вимогами високої доступності та низького часу відгуку. Реалізація подієво-орієнтованого вводу-виводу вбудована в Erlang-систему. Це є перевагою при проєктуванні паралельних додатків.[17]
Стандартна бібліотека містить модуль 1> io:format("приклад виводу : ~p~n", [1]).
приклад виводу : 1
ok
Функції модуля БібліотекиСтандартна бібліотека модулівЗгідно з офіційною документацією, стандартна бібліотека модулів STDLIB[14] є обов'язковою для включення до мінімальної системи Erlang/OTP[26] разом з ядром Erlang. Бібліотека містить модулі, що надають різноманітні функції для роботи з вбудованими типами та іншими структурами даних, вводу-виводу, звернення до середовища, для роботи з файловою системою, процесами тощо. Згідно з офіційною документацією, стандартна бібліотека модулів STDLIB є обов'язковою для включення до мінімальної системи Erlang/OTP[26] разом з ядром Erlang. Бібліотека містить модулі, що надають різноманітні функції для роботи з вбудованими типами та іншими структурами даних, вводу-виводу, звернення до середовища, для роботи з файловою системою, процесами тощо. Модуль
Модуль Модуль Модуль Модуль Модуль Модуль Модуль Модуль Модуль
Модуль
Модуль Модуль Модуль
Модуль Окрім цих найбільш важливих модулів, стандартна бібліотека містить багато інших, з якими можна познайомитися за документацією.[8] Таблиці ETS та DETSДля організації колекцій в оперативній пам'яті в Erlang є модуль ETS (англ. Erlang Term Storage — «сховище термів Erlang»). ETS може зберігати чотири види колекцій: множина (англ. set), впорядкована множина (англ. ordered set), мультимножина (англ. bag), мультимножина з повтореннями (англ. duplicate bag).[9] Доступ до елементів колекцій відбувається ключовим полем кортежу(ключі, як і значення, можуть бути будь-яких типів). Упорядковані множини зреалізовані у вигляді бінарних збалансованих АВЛ-дерев, а інші колекції — з використанням хеш-таблиць.[8] DETS-таблиці доповнюють функціональність ETS-таблиць (за винятком впорядкованих множин), дозволяючи зберігати дані у файлах. Фреймворк OTPOTP (англ. Open Telecom Platform) є налагодженим набором корисних поведінок (англ. behaviours) процесів. Він використовується для створення серверних програм. OTP формалізує дії процесів та дозволяє будувати на їх основі OTP-додатки. У модулях ОТР визначено загальні, стандартизовані шаблони для конструювання паралельних додатків. OTP-поведінки поділяються на робочі процеси (англ. worker processes):
У завдання процесів спостерігачів входить стеження за робочими процесами та іншими процесами спостерігачами — нащадками. Дерева спостерігачів складають OTP-додаток (англ. application). Документація Erlang визначає OTP-додаток компонентом, який реалізує деяку функціональність, яка може бути незалежно запущена на виконання і зупинена як ціле, а також повторно використана в інших системах.[30] Розробник програми пише код модулів функцій зворотного виклику (англ. call-back module), в яких і знаходиться специфічна для цієї програми частина функціональності.[8] Строго кажучи, OTP не є частиною мови Erlang. Однак, він настільки увійшов у культуру та практику розробників на Erlang, що часом між ними складно провести межу.[9] Розробка графічного інтерфейсу користувачаРозробка додатків з графічним інтерфейсом користувача (крім веб-інтерфейсів) може вестись за допомогою бібліотеки wxErlang — бібліотеки wxWidgets, портованої для Erlang. В стандартну поставку Erlang/OTP входить WxErlang. WxWidgets написаний на C++, тому перед розробниками wxErlang стояло завдання виразити засобами Erlang ієрархію об'єктів. Спрощуючи, у wxErlang класам відповідають модулі, а об'єктам — посилання, макросам на C ++ відповідають макроси Erlang. Деякі типи даних, для яких у C++ були використані класи, подаються в Erlang за допомогою інших типів даних, наприклад wxPoint задається у вигляді кортежу з двох елементів. Події wxErlang можна оброблити в Erlang або через функцію зворотнього виклику (англ. call-back functions), або в більш природному середовищі Erlang передачею повідомлень.[8] Програмування на ErlangІнтерактивна оболонкаІнтерактивна оболонка (англ. shell) для Erlang може бути викликана в Unix-подібних системах командою Оболонка дозволяє:
Також, оболонка дозволяє використовувати додаткові функції (команди), доступні тільки в ній. До прикладу, команда В оболонці можна викликати BREAK-меню за допомогою Ctrl+C (у Unix-подібних ОС) або Ctrl+Break (у Windows). Це меню має різні команди, у тому числі a — негайна зупинка, c — продовження роботи в оболонці та інші інформаційні та допоміжні команди для роботи з Erlang-системою. Комбінація клавіш Ctrl+G викликає ще командне меню, яким можна, за необхідності, зупинити процес, що «завісив» оболонку, і повернутися в оболонку (i і потім c)[13] Документування та оформлення кодуСистема EDoc може генерувати документацію з вихідного коду. Для документування коду модуля достатньо додати певним чином розмічений текст, а також файл Типи та аналіз коду
Програма Dialyzer, розроблена в рамках проєкту HiPE. Вона входить до стандартної поставки і дозволяє виявити помилки (у тому числі помилки типізації) шляхом статичного аналізу коду. Програма TypEr, написана Тобіасом Ліндалом (Tobias Lindahl) та Костісом Сагонасом (Kostis Sagonas), є частиною Dialyzer. TypEr дозволяє перевіряти визначення типів функцій, звіряти вказаний у директиві -type(user_status() :: disabled | enabled). % статус - один з двох атомів
-record(user, {login="anon" ::string(), % типи полів запису
password ::string(),
status :: user_status(),
nickname ::string()}).
-spec(check_password(string(), #user{}) -> ok | {error, string()}). % декларація функції
check_password(Password, User) -> % визначення функції
...
Dialyzer (від англ. DIscrepancy AnaLYZer for ERlang Programs — «аналізатор суперечностей для Erlang-програм») програма для виявлення в коді окремих модулів і в окремих додатках, помилок типів, недосяжного коду та надлишкових перевірок. Усі виявлені Dialyzer дефекти вимагають усунення, бо інструмент не дає помилкових спрацьовувань. Для кожної функції всіх модулів, що перевіряються, Dialyzer, встановлює тип, використовуючи заснований на обмеженнях вивід типів і аналіз потоків даних. Після визначення типів функцій проводиться аналіз протиріч у програмі.[32] Тестування, профілювання, рефакторингДля модульного тестування Erlang надає EUnit. Для системного тестування Erlang надає фреймворк Common Test. EUnit містить засоби для опису тестів, в тому числі необхідний для цього набір макросів, а також виводить звіт по закінченню тестування. Тестування модулів відбувається шляхом підключення заголовного файлу з EUnit, а функції з тестами можуть бути як включені в сам модуль, що тестується, так і винесені в окремий модуль.[8] Тестування паралельних програм можна виконати за допомогою Quviq Quick Check (версія Mini безкоштовно). Крім тестування, можна здійснити перевірку всіх можливих варіантів вихідних даних за допомогою методу перевірки моделей. Для цього є, створена в Мадридському політехнічному університеті, утиліта McErlang. [1][8] Щоб профілювати код та виявити ступінь покриття коду тестами можна звернутися до модулів Для Erlang розроблена низка інструментів рефакторингу вихідного коду (список не є вичерпним):
Утиліта tidier дозволяє автоматично знаходити та виробляти еквівалентні перетворення коду, наприклад, замінює lists:filter(fun (X) -> is_something(X) end, L)
на [X || X <- L, is_something(X)]
ЕфективністьErlang має свої секрети написання ефективного коду. Удосконалення мови робить деякі з трюків застарілими, тому документація є найкращим посібником у питаннях оптимізації, у сукупності з профілюванням та стрес-тестуванням. Наприклад, при роботі зі списками не бажано додавати елемент до кінця довгого списку за допомогою конкатенації або функції додавання елемента до списку. Зате можна додати елемент до початку списку, а кінцевий результат обробити функцією звернення порядку елементів списку. L1 = [New_Elem | List].
L2 = lists:reverse(L1).
Є свої рекомендації і для підвищення ефективності паралельних програм. До прикладу, дії, що вимагають багато пам'яті, найкраще виділяти в окремий процес, бо при цьому зменшуються витрати на збирання сміття. Тоді пам'ять швидше буде звільнено після завершення процесу.[8] Erlang та інші мови програмуванняІнтеграція та гібридні мовиErlang-система дозволяє виконувати інтеграцію з системами на інших мовах програмування. Є механізми для мережевої взаємодії з С, Java, Лісп, Perl, Python, Ruby. До прикладу, для ефективнішого синхронного виклику невеликих функцій на C можна використовувати платформно-залежні функції (англ. NIF, natively implemented function). Високорівневі бібліотеки дозволяють Erlang-системі представляти C або Java-вузли як звичайні Erlang-вузли. Інші мови можна пов'язати з середовищем виконання Erlang за допомогою драйверів або мережевих сокетів за допомогою протоколів на кшталт HTTP, SNMP, IIOP. До прикладу, Ruby взаємодіє з Erlang за допомогою пакета erlectricity, а для Python розроблена реалізація Erlang-вузла у вигляді пакету py-interface.[20] Віртуальна машина Erlang знаходить застосування і в інших мовах програмування. До прикладу, Elixir та проєкт Erl2 Джо Армстронга. Крім того, Роберт Вірдінг підтримує проєкт Lisp Flavored Erlang («Erlang, приправлений Ліспом»), у якому синтаксис Ліспа використовується з компілятором[34] Erlang. Є й інші BEAM-мови: Efene, Joxa, Reia[35], Luerl, Erlog[36]. На офіційному сайті мови є згадка проєкт про Erjang, в якому використовується віртуальна машина Java. Порівняння Erlang та C++ за продуктивністюПрактика показує, що для вивчених телекомунікаційних додатків код на Erlang був на 70—85 % коротшим, ніж на C++, а продуктивність системи при переписуванні коду з C++ на Erlang зросла майже на 100 %[37]. Для одного із використаних у дослідженні проєктів різниця була пояснена написанням додаткового C++ коду в рамках захисного програмування, управління пам'яттю та коду для високорівневої комунікації, тобто можливостями, які є частиною мови Erlang та бібліотек OTP.[8] Порівняння взаємодії процесів у Erlang та GoВплив теорії послідовних процесів, що взаємодіють, Чарльза Е.Хоара відчувається як у Go, так і в Erlang. Erlang і Go Спільне : процеси відправляють одне одному повідомлення. Відмінне:
Організація взаємодії з повідомленнями У Erlang немає типізації часу компіляції за винятком охоронних виразів, що дозволяє посилати процесам повідомлення будь-якого типу. «Незрозуміле» повідомлення буде проігноровано, або назавжди залишиться в черзі[38]. Go дозволяє легко організувати групу «go-програм» (англ. goroutine — натяк на англ. co-routine — співпрограма) для отримання повідомлень з деякого каналу (такий підхід відомий як пул потоків). Реалізація робочого пулу В Erlang, при проєктуванні якого приділялася особлива увага детермінізму та часу затримки (англ. latency), реалізація робочого пулу можлива, але потребує додаткових зусиль. Численні відправники тривіально реалізуються в обох мовах.[38] Erlang-процес може надіслати повідомлення і чекати на нього відповідь (відповідно до деякого зразка), ігноруючи інші повідомлення в черзі. В Go таке неможливо, але подібна функціональність може бути досягнута створенням (у тому числі динамічним) нових вводів, тобто поділом каналів за призначенням.[38] Розділення між процесами В Erlang відсутній стан, що розділяється між процесами (англ. shared mutable state) і тому ізольований процес дуже рідко представляє інтерес[38] Go вимагає явної вказівки того, які go-програми будуть взаємодіяти з іншими передачею повідомлень. Абстракції взаємодіючих процесів схожі в Erlang і Go. Щоб уникнути помилок при переході з однієї мови на іншу, слід враховувати тонкощі і особливості обох мов: шаблони, які хороші в одній мові, можуть не підходити для іншої.[38] КритикаВ Erlang є недоліки[40]:
Джо Армстронг, один з творців мови, у своєму виступі на конференції з історії мов програмування в 2007 році перерахував список зон зростання для мови:[41]
Масове поширення Erlang може стримувати незвичний для більшості програмістів синтаксис, використання функціональної парадигми, а також те, що найкраща на 2022 рік реалізація мови використовує віртуальну машину BEAM, а не більш поширену JVM.[42] Сфера застосуванняErlang підходить для:
Erlang погано підходить для: Написання коду, що містить інтенсивні обчислення з плаваючою комою, який вимагає включення нативного коду конкретної платформи чи сильної оптимізації і для створення додатків, які потребують синхронного виконання завдань. Для проєктів, в яких код повинен виконуватися на JVM або CLR, або проєктів, що вимагають безлічі бібліотек з інших систем програмування[44]. Крім того, Erlang став застосовуватися для :
Erlang часто ставлять у заслугу легендарну надійність ATM-комутатора AXD301 у мережі British Telecom. За даними Ericsson, з січня 2002 року за декілька років трапилася лише одна незначна несправність, на підставі чого надійність системи згідно з розрахунками була 99,9999999 %[49]. Хоча більш реальні оцінки, що враховують багато інших факторів, говорять все-таки про «п'ять дев'яток», успіх маршрутизатора пов'язують з доступними засобами розробки надійних паралельних обчислень, вбудованими в Erlang[49]. Ще Erlang використовується в додатках з відкритим вихідним кодом, наприклад:
Для Erlang було написано декілька веб-серверів:
Ще були створені декілька веб-фреймворків і систем управління вмістом, таких як:
Серед відомого програмного забезпечення, написаного Erlang, яке не увійшло в категорії вище виділяють:
Крім того, написаний на Erlang інструмент Tsung, який дозволяє емулювати тисячі (за достатньої кількості тестових серверів — мільйони) одночасних користувачів, застосовується у стрес-тестуваннях розподілених систем[61]. Erlang підходить для завдань штучного інтелекту (особливо обчислювального інтелекту, нейроеволюції), заснованих на нейронних мережах. Таке застосування можливе завдяки п'ятьом ключовими властивостями «мови програмування нейронних мереж», що є у Erlang:
Прикладом такого застосування є реалізація одного з підходів до нейроеволюції — DXNN[62]. СпільнотаНавколо технологій Erlang утворилась спільнота розробників, яка підтримує новачків. Вихідний код Erlang доступний через сервіс спільної розробки GitHub. Розробники та користувачі Erlang можуть спілкуватися через список розсилки Erlang-questions (питання Erlang) або на IRC-каналі #erlang на Freenode. Erlang Factory влаштовує у всьому світі заходи та конференції, серед яких конференція користувачів Erlang (Erlang User Conference). Спеціальна група SIGPLAN ACM регулярно проводить Erlang-майстерню (Erlang Workshop), а конференція OSCON включає секцію з Erlang[63]. В Україні Erlang спільнота порівняно невелика. Див. такожВиноски
Посилання
|
Portal di Ensiklopedia Dunia