Erlang

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку
Erlang
Erlang logo.png
Парадигма мультипарадигмальна: паралельна, функціональна
Дата появи 1987
Творці Джо Армстронг (програміст)
Розробник Ericsson
Останній реліз 25 (18 червня, 2022; 53 дні тому (2022-06-18))
Система типізації динамічна типізація, сильна типізація
Під впливом від Prolog, SmallTalk
Вплинула на F#, Clojure, Rust, Scala, Opa, Reia
Операційна система Багато-платформова
Ліцензія Apache 2.0
Звичайні розширення файлів .erl
Репозиторій вихідного коду github.com/erlang/otp
Вебсайт www.erlang.org
CMNS: Erlang у Вікісховищі

Erlang (Ерла́нґ) — мова функційного програмування з динамічною типізацією, призначена для розробки програм для різного роду розподілених і багатониткових систем. Розроблена і підтримується компанією Ericsson. Мова містить в собі засоби породження паралельних процесів та їхньої взаємодії за допомогою посилання асинхроних повідомлень, без використання блокувань.

Мова орієнтована для розробки розподілених відмовостійких застосунків, які забезпечують паралельну обробку запитів в режимі реального часу. Мова набула поширення в таких областях, як телекомунікації, банківські системи, електронна комерція, комп'ютерна телефонія і організація миттєвого обміну повідомленнями.

Програма транслюється в байт-код, що виконується віртуальною машиною, що забезпечує переносність. Одночасно розробниками випускається OTP (Open Telecom Platform) — супутній набір бібліотек і компонентів для розробки розподілених систем на мові Erlang. Код проєкту поширюється під модифікованою вільною ліцензією MPL.

Свій синтаксис і деякі концепції мова Erlang успадкувала від мови логічного програмування Пролог. Мова підтримує багато типів даних: умовні конструкції, зіставляння зі взірцем, обробку виключень, спискові включення і вираз бітових рядків,функції (анонімні функції, функції вищого порядку, рекурсивні визначення функцій, оптимізацію хвостової рекурсії, модулі, прийом і відправку повідомлень між процесами.Передпроцесор підтримує роботу з макросами і включення заголовкових файлів.

Популярність Erlang зросла у зв'язку з розширенням області його використання (телекомунікаційні системи) на високонагружені паралельні розподілені системи, які обслуговують мільйони користувачів WWW, такі як чати, системи керування веб-контентом, веб-сервери і розподілені, бази данних, що вимагають масштабування. Erlang використовуються в деяких NoSQL-базах даних високої доступності.

Назва і історія[ред. | ред. код]

Серед людей малообізнаних з історією мови Ерланг поширена думка, що назва «Erlang» розшифровується як ERicsson LANGuage. Насправді мова отримала свою назву на честь Агнера Крарупа Ерланга[en], данського математика, який працював у галузі телекомунікацій. Існує одиниця вимірювання телекомунікаційного трафіку, яка також називається Erlang[en].[1]

На створення Erlang вплинули ML, Miranda, Ada, Модула-2,CHILL,Пролог. Крім того, на спосіб оновлення програмного забезпечення вплинува Smalltalk і пропрієтарні мови EriPascal та PLEX, якими послуговувались в Ericson.

Мова Erlang була створена в 1986 році в лабораторії компанії Ericsson спільно Джо Армстронгом (Joe Armstrong), Робертом Вірдінгом (Robert Virding) і Майком Вільямсом (Mike Williams), і в 1998 році переведена в розряд відкритих проєктів. Завдяки початковій орієнтації на створення застосунків для паралельної обробки запитів в режимі реального часу мова набула поширення в таких областях як телекомунікації, банківські системи, електронна комерція, комп'ютерна телефонія і організація миттєвого обміну повідомленнями.

в 1998 році топ-менеджмент Ericsson вирішив не брати на себе зобов'язань з розробки та підтримки власної мови програмування, натомість зосередившись на Java . Використання Erlang було заборонено у нових проектах Ericsson Radio AB у зв'язку з реалізацією плану аутсорсингу програмної технології компанії Rational Inc.

Це рішення дуже сильно вплинуло на майбутнє Erlang: воно призвело до відкриття коду Erlang під відкритою ліцензією EPL (аналог Mozilla Public License ) , а також послужило головною причиною початку поширення мови за межами компанії, що його створила. Основним запереченням проти відкриття вихідного коду було вирішення питань щодо патентів, але ці труднощі були подолані. Незабаром багато хто з основних розробників покинув Ericsson, щоб організувати власний бізнес - Bluetail AB.

На початку 2000-х років наукові кола стали виявляти інтерес до Erlang. З 2002 року став проводитись щорічний Erlang Workshop.Ericsson продовжував спонсорувати проект HiPE (від англ. High-Performance Erlang - високопродуктивний Erlang). Проект HiPE займався ефективною реалізацією мови та інструментами для перевірки типів, а з 2001 року створений у групі проекту компілятор в машинний код входить у постачання версії Erlang/OTP, що вільно розповсюджується. Роботи, пов'язані з Erlang, ведуть інші заклади вищої освіти. Інструменти для рефакторингу створені в Кентському університеті у Великій Британії та університеті Лоранда Етвеша в Угорщині, інструменти для різних видів тестування — у Мадридському політехнічному університеті, технічному університеті Чалмерса та Гетеборгському університеті.

Коли системи з симетричною багатопроцесорністю тільки починали завойовувати ринок серверів і настільних комп'ютерів, кидаючи виклик розробникам програмного забезпечення, вже в 2006 перша версія Erlang з підтримкою SMP була випущена спільними зусиллями команди OTP з Ericsson і команди HiPE . Незабаром після цього вийшла перша майже за десятиліття велика монографія з Erlang: «Programming Erlang» Джо Армстронга , після чого багато розробників «відкрили» для себе Erlang/OTP, і мова стала набирати популярності.

Процес розвитку мови включає у себе розгляд пропозицій з розвитку EEP (Erlang Enhancement Proposal). За допомогою цих пропозицій Erlang-спільнота вносить зміни у стандартну подачу Erlang. З внесеними пропозиціями можна ознайомитися на веб-сторінці erlang.org/eeps.

Філософія[ред. | ред. код]

За свідченнями Майка Вільямса, Erlang створювався для вирішення трьох проблем розробки розподілених систем м'якого реального часу з високим ступенем паралелізму: можливості швидкої та ефективної розробки ПЗ; отримання системи, стійкої до програмних та апаратних збоїв, та можливості оновлення системи «на льоту», без простою обладнання.

За словами Вільямса, філософія якої дотримувались розробники Erlang пасує і для написання програмного забезпечення цією мовою.

  • Знайдіть правильні методи. Проектуйте з використанням прототипів.
  • Самих ідей мало: треба вміти їх реалізовувати і знати, що вони працюють.
  • Робіть помилки в невеликому масштабі, а не у виробничому проекті.

Мовою оригіналу:

  • Find the right methods — Design by Prototyping.
  • It is not good enough to have ideas, you must also be able to implement them and know they work.
  • Make mistakes on a small scale, not in a production project.

Більшість мов, створених раніше Erlang, були розроблені без попереднього знаходження застосування, тоді як Erlang був розроблений спеціально на основі вимог до розподілених, стійких до відмов, паралельних систем реального часу. З розвитком мережі Інтернет виявилося, що багато програм мають аналогічні вимоги, що пояснює зростаючий інтерес до мови.

Секрет високої стійкості до відмов полягає у використанні ізольованих один від одного легковісних процесів, пов'язаних лише механізмом обміну повідомленнями та сигналами виходу. Принципи розробників на Erlang стосовно обробки помилкових ситуацій у процесах, можна виразити у вигляді висловлювання : "Let it crash and let someone else deal with it." або скорочено: "Let it crash". Пов'язано це з тим, що в Erlang-системі легко стежити за завершенням процесу, завершувати процеси, пов'язані зі збійним, і запускати нові процеси.

Особливості[ред. | ред. код]

Синтаксис успадкований від мови Prolog.

Підтримує модулі, поліморфні функції, зіставлення за шаблоном, анонімні функції, умовні конструкції, структури, обробку винятків, оптимізацію хвостової рекурсії.

Головна риса Erlang — модель легких процесів. Процеси є дешевими, їхнє створення вимагає таку кількість ресурсів, що їх можна порівняти з викликом функції. Єдиним способом взаємодії процесів є асинхронний обмін повідомленнями.

Процес може встановити зв'язок (link) з іншими процесами і за вибором або отримувати повідомлення про їхнє дострокове завершення з вказанням причини або розділити їхню долю. Процес має свою «поштову скриню», звідки може вибірково читати повідомлення.

Мова програмування Ерланг сприяє створенню великої кількості конкурентних процесів. Процеси ізольовані і не мають спільного стану.

Процес проєктування полягає в ітеративному розбитті системи на ієрархії підсистем, які конкурентно взаємодіють, доки складові не стануть достатньо простими для реалізації.

Сув'язь «Процеси+повідомлення» на відміну від сув'язі «Об'єкти+Інтерфейси+Успадкування» часто дає компактніші рішення. Відсутність потреби блокування доступу до стану процесу для синхронізації їхньої взаємодії суттєво спрощує програмування. Для конкурентного ресурсу зазвичай створюється процес-монітор, через який здійснюється взаємодія з ресурсом.

Також важливий момент полягає у формулі «let it crash» («нехай процес впаде»). Замість перехоплювати помилки і намагатися продовжувати роботу частина програми, що містить ризикований код, відокремлюється у незалежний процес-камікадзе, який робить все можливе, щоб система вбила його у випадку виникнення помилки, а батьківський процес тільки отримує повідомлення про смерть нащадків і робить висновки. Такий підхід позбавляє код численних перевірок.

Високорівневі конструкції[ред. | ред. код]

Erlang є декларативною мовою програмування, яка використовується для опису того, що має бути обчислено ніж як . Наприклад, визначення функції, яке використовує зіставлення зі зразком для вибору одного з варіантів обчислення або вилучення елемента даних зі складової структури нагадує рівняння . Зіставлення із зразком поширене навіть на бітові рядки, що спрощує реалізацію телекомунікаційних протоколів.

Функції є об'єктами першого класу Erlang. У мові також широко застосовуються характерні для функціональної парадигми програмування спискові включення (генератори списків)

Паралельні обчислення[ред. | ред. код]

Обмін повідомленнями в мові Erlang

Особливістю мови Erlang є застосування легковісних процесів відповідно до моделі акторів . Такий підхід дозволяє виконувати одночасно сотні тисяч і навіть мільйони таких процесів, кожен з яких може мати скромні вимоги до пам'яті. Процеси ізольовані один від одного і не мають загального стану, але між ними можна встановити зв'язок та отримувати повідомлення про їхній стан.

Для взаємодії процесів використовують асинхронний обмін повідомленнями. Кожен процес має свою чергу повідомлень, обробка якої використовує зіставлення із взірцем. Процес, що надіслав повідомлення, не отримує повідомлення про доставку, навіть якщо ідентифікатор процесу одержувача недійсний або одержувач ігнорує повідомлення. Таким чином, відповідальність за правильно організовану взаємодію між процесами лежить на розробникові.

Наприклад, при реалізації на Erlang мережевого чату структура програми може безпосередньо відображати одночасність дій користувачів щодо обміну повідомленнями шляхом запуску нових процесів. Ефективність передачі повідомлень зберігається і зі збільшенням кількості процесів, а вимоги до пам'яті мінімізуються рахунок того, що легковісними процесами управляє віртуальна машина, а не засоби операційної системи.

Розподілені обчислення[ред. | ред. код]

Erlang з самого початку проектувався для розподілених обчислень та масштабованості . Розподіл обчислень вбудовано синтаксис і семантику мови, тому побудова системи можна вести, абстрагуючись від конкретного місця обчислень. У стандартній поставці Erlang може налагодити зв'язок процесів протоколу TCP/IP незалежно від підтримуваних ним нижче платформ (операційних систем)

Запущений екземпляр емулятора Erlang називається вузлом (node). Вузол має ім'я і «знає» про існування інших вузлів на цій машині або у мережі. Створення та взаємодія процесів різних вузлів не відрізняється від взаємодії процесів всередині вузла. Для створення процесу на іншому вузлі процесу досить знати його ім'я. Програми, написані на Erlang, здатні працювати на кількох вузлах. Вузлами можуть бути процесори, багато ядр одного процесора, і навіть цілий кластер машин. Для створення процесу на іншому вузлі процесу достатньо знати його ім'я і, без особливих підстав, він може не цікавитися фізичним розташуванням взаємодіючого з ним процесу. Синтаксис відправки повідомлення процесу на своєму вузлі та віддаленому - один і той же.

Завдяки вбудованим у мову можливостям розподілених обчислень об'єднання в кластер, балансування навантаження, додавання вузлів та серверів, підвищення надійності викликають лише невеликі витрати на додатковий код. За замовчуванням, вузли спроектовані для роботи всередині відокремленого сегмента мережі ( DMZ ), але, якщо необхідно, комунікація між вузлами може відбуватися із застосуванням протоколу SSL, захищеного криптографічними методами.

М'який реальний час[ред. | ред. код]

Програми високорівневою мовою Erlang можуть бути використані в системах м'якого реального часу (який іноді перекладають як «псевдореальний» або «квазіреальний»). Автоматизоване керування пам'яттю та складання сміття діють у рамках одного процесу, що дає можливість створювати системи з мілісекундним часом відгуку (навіть незважаючи на необхідність складання сміття), що не відчувають погіршення пропускної здатності при високому навантаженні.

Гаряча заміна коду[ред. | ред. код]

Для систем, які не можуть бути зупинені для оновлення коду, Erlang пропонує гарячу заміну коду (англ. hot code upgrade ). При цьому в додатку можуть одночасно працювати старі і нові версії коду. У такий спосіб програмне забезпечення на Erlang може бути модернізовано без простоїв, а виявлені помилки виправлені.

Опис мови[ред. | ред. код]

Типи даних[ред. | ред. код]

Типізація в Erlang є сильною та динамічною . Динамічна типізація була обрана для мови Erlang через те, що перші розробники були більше з нею знайомі . На думку Джо Армстронга, статична типізація зажадала б дуже великих трудовитрат, зокрема, реалізувати систему гарячого дозавантаження коду було б дуже важко. Така типізація, коли можливі помилки типів виявляються лише під час виконання, не завадила можливості створювати системи з дуже високим рівнем доступності. Дані Erlang є незмінними : операції не переписують старі значення, що знаходяться в пам'яті. Якщо необхідно, модулі на Erlang можна забезпечити описами та визначеннями нових типів (що не впливають на компіляцію програми) для автоматичної перевірки типів за допомогою утиліти Dialyzer.

Числа[ред. | ред. код]

У Erlang є два типи числових літералів : цілі і з плаваючою комою, наприклад: 125, 4.5e-20 . Крім звичайної нотації, числа можна задавати через символ ASCII (наприклад, $B означає 66 ) або разом із зазначенням системи числення з основою від 2 до 36 (у старих версіях - до 16), наприклад: 16#3f, 2#1010 . У Erlang застосовуються цілі числа довільної точності та дійсні числа подвійної точності (64 біти ), у стандарті IEEE 754-1985.

Для роботи з числами можна використовувати модуль math, який містить звичайний набір математичних функцій та функцію math:pi/0, що повертає число . Приклад обчислень в інтерактивній оболонці:

Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:4:4] [async-threads:0] [kernel-poll:false]

Eshell V5.9.1 (abort with ^G)
1> 123/23 + 12*(2+3).
65.34782608695652
2> math:cos(math:pi()).
-1.0
3> random:uniform(10).
5

Атоми[ред. | ред. код]

Атом — константа з ім'ям, яка повинна бути поміщена в одинарні лапки, якщо не починається з малої літери або містить знаки, окрім літер, цифр, підкреслення, крапки та символу @ . Поняття атома запозичене з Прологу та його можна вважати аналогом перерахувань (enum) в інших мовах програмування (без необхідності попередньої декларації) [2] . Атоми використовуються майже виключно в порівняннях, що мають Erlang дуже ефективну реалізацію. Крім того, деякі атоми мають певний сенс у значеннях, що повертаються, і описі винятків. До них відносяться, наприклад : error, ignore, noreply, ok, reply, stop, undefined.

Бітові рядки та бінарні дані[ред. | ред. код]

Бітовий рядок використовується для зберігання пам'яті нетипізованих даних. Рядки, що складаються з цілої кількості октетів, називаються бінарними (або двійковими ) даними (англ. binaries ). Синтаксис опису бітового рядка досить гнучкий, оскільки визначає значення бітів окремих діапазонів і може бути забезпечений модифікатором типу. Декілька прикладів в інтерактивній командній оболонці:

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 послідовно отримує біти числа 2#111011, які потім інвертуються операцією бітового заперечення bnot (від англ. binary NOT ), внаслідок чого виходить число 4.

Кортеж[ред. | ред. код]

Кортеж (англ. tuple ) - складовий тип даних з фіксованою кількістю елементів. При доступі до елементів кортежу за допомогою вбудованих функцій, нумерація елементів починається з одиниці, а не з нуля. Перший елемент кортежу прийнято використовуватиме вказівки ролі кортежу у програмі. Якщо перший елемент атом, його називають тегом (англ. tag - "мітка"). У Erlang прийнято будувати різні типи даних на основі кортежів з тегами, що полегшує налагодження програми та вважається гарним стилем програмування.

Для роботи з кортежами є кілька вбудованих функцій, наприклад:

1> tuple_size({a, 1, "777"}).
3
2> element(1, {b, 2, 3, 4}).
b
3> setelement(1, {c, 5}, d). 
{d,5}

Список[ред. | ред. код]

Список (англ. list ) - складений тип даних, що містить змінну кількість елементів. Для маніпуляції зі списками можна використовувати функції модуля lists стандартної бібліотеки. Формально список визначається як той, хто має голову (англ. head ) та хвіст (англ. tail ), що виражається синтаксично у вигляді [HEAD|TAIL], де хвіст зазвичай є списком (можливо порожнім). Порожній список позначається []

Списки можна записувати і більш звичним способом. Наступні записи еквівалентні:

1> [a|[b|[c|[]]]].
[a,b,c]

Для роботи зі списками можна використовувати спискові включення (генератори списків), наприклад:

1> [ X / 2 || X <- [1,2,3,4]].
[0.5,1.0,1.5,2.0]

Модуль lists стандартної бібліотеки містить функції для обробки списків (і рядків, так як Erlang рядок є списком), такі як знаходження максимуму, сортування, зміна порядку елементів на протилежний, підсумовування елементів і т. п. У наступному прикладі два списки склеюються операцією конкатенації, а потім розбиваються на дві частини функцією lists:split/2 :

1> lists:split(2, [l,2,3]++[4,5,6]).
{[l,2],[3,4,5,6]}

У модулі lists є також набір функцій вищого порядку, таких як lists:all/2, lists:any/2, lists:dropwhile/2, lists:filter/2, lists:foldl/3, lists:foldr/3, lists:map/2, lists:foreach/2 . Наступний приклад ілюструє роботу функції 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). Другий параметр foldr дає початкове значення так званого акумулятора. Для кожного елемента списку (справа ліворуч) до елемента та акумулятора застосовується функція, задана першим аргументом foldr, а значення записується в акумулятор. За вичерпанням списку, функція повертає значення акумулятора. Функція є досить потужним засобом, якщо врахувати, що акумулятор може бути переліком або кортежем.

Рядок[ред. | ред. код]

У Erlang немає самостійного типу для рядків : внутрішньо рядки надаються списками. Синтаксично рядок можна задати лапками. Так, "Привіт!" рівносильний (у відповідному кодуванні) списку [1055,1088,1080,1074,1077,1090,33] . Erlang підтримує Unicode як у рядку, так і запису окремого знака (через $ )

Атоми і рядки зовні досить схожі, але мають різні реалізації. Тоді як атоми можна лише порівнювати, рядки підтримують багато інших операцій, їм є безліч функцій в модулях lists і string . Рядок може виконувати функції атома, але пам'ять, займана рядком пропорційна її довжині, тоді як атоми зберігаються в системній таблиці і на кожне використання атома в програмі припадає лише пара байтів, незалежно від довжини атома. Порівняння двох атомів - це порівняння двох внутрішніх ідентифікаторів, яке виконується за одну операцію, тоді як порівняння рядків передбачає поелементний прохід елементів рядків.

Логічні значення[ред. | ред. код]

Для значень істина та брехня в Erlang застосовуються атоми true (істина) і false (брехня), які використовуються операціями порівняння, логічними операціями, вбудованими функціями. Приклад:

1> 2 < 3. 
true
2> is_boolean(125).
false

Функціональний об'єкт (Fun)[ред. | ред. код]

Fun-вираз дозволяє створити анонімну функцію, наприклад, для передачі як параметр іншим функціям. За допомогою fun можна також отримати функціональний об'єкт для функції модуля [3] . Приклади:

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 опис може бути наступним

-record(user, {login="anon", password, nick}).

З цього опису компілятор дізнається, що маються на увазі кортежі з чотирьох елементів, в яких елементи з другого по четвертий відповідають полям login password nick (порядок важливий) запису з ім'ям user (визначається атомом в першому елементі кортежу). Значенням за промовчанням для поля login є рядок "anon" . Якщо значення за промовчанням не вказано явно, мається на увазі атом undefined. Створення записів та вилучення елементів запису завжди вимагає явної вказівки імені запису:

R0 = #user{} % всі поля отримують значення за замовчуванням
R1 = #user{login="user1", password="secret", nick="john"} % всі поля отримують значення

Асоціативний масив[ред. | ред. код]

Асоціативний масив (словник) зберігає пари виду "(ключ, значення)". Як ключ, так і значення, може виступати будь-який терм Erlang.

Map = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "hi" => 42},
Key = "hi",
maps:find(Key,Map).
{ok,42}

Інші типи[ред. | ред. код]

У мові Erlang є інші типи даних. Тип посилання (англ. reference ) є практично унікальним в середовищі часу виконання Erlang. Посилання створюється викликом функції make_ref/0 і може повторитися через 282 викликів цієї функції. Посилання можна порівнювати на рівність, а застосовуються вони для одноразових позначок або " чарівного печива "

Ідентифікатор порту (англ. port identifier ) визначає порт для зв'язку із зовнішнім по відношенню до Erlang-системи світом. Порт дозволяє процесу, що його створив-власнику (так званому приєднаному процесу) обмінюватися бінарними повідомленнями зі сторонніми програмами і ОС способом, прийнятим в даній операційній системі.

Ідентифікатор процесу (англ. PidPid ), як і випливає з його назви, ідентифікує процес, що породжується різними функціями spawn . Ідентифікатор можна вважати унікальним під час роботи Erlang-системи, але в системах, що довго працюють, можуть все-таки бути використані повторно, що зазвичай не є проблемою на практиці.

Вбудовані функції для роботи з типами[ред. | ред. код]

Для перетворення типів використовуються вбудовані функції (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 надає найбільш поширені арифметичні операції для цілих чисел і чисел з плаваючою комою :

Позначення Операція, що виконується приклад Результат прикладу
+ Унарний плюс +3 3
- Унарний мінус -3 -3
+ Додавання 2+3 5
- Віднімання 7-3 4
* множення 1.2*0.4 0.48
/ Поділ 5 / 3 1.6666666666666667
div Цілочисельний поділ 5 div 3 1
rem Цілочисельний поділ із залишком 5 rem 3 2

Всі ці операції лівоасоціативні . Унарні операції мають найвищий пріоритет, потім слідує множення і розподіл, найменший пріоритет у складання та віднімання. При необхідності ціле може призводитися до типу з плаваючою комою.

Бітові операції[ред. | ред. код]

Бітові операції працюють над цілими числами і дають в результаті ціле число.

Позначення Операція, що виконується приклад Результат прикладу
bnot Побитове заперечення bnot (2#1000) -9
band Побітове І 2 band 3 2
bor Побітове АБО 1 bor 2 3
bxor Побітове виключне АБО 5 bxor 3 6
bsr Побітове зрушення вправо 32 bsr 2 8
bsl Побітове зрушення вліво 1 bsl 5 32

Логічні операції[ред. | ред. код]

Логічні операції працюють над логічними значеннями true (істина) і false (брехня), одержуваними в результаті порівнянь та застосування функцій перевірки типу

Позначення Операція, що виконується приклад Результат прикладу
not Заперечення (НЕ) not true false
and Кон'юнкція (І) true and (1 < 5) true
andalso Аналогічно and, але не обчислює другий операнд, якщо перший false false andalso (1 < 5) false
or Диз'юнкція (АБО) is_atom("1") or is_atom(1) false
orelse Аналогічно or але не обчислює другий операнд, якщо перший true true orelse (1 < 5) true
xor Виключне АБО true xor true false

Операції порівняння[ред. | ред. код]

Операції порівняння отримують два операнди, а результатом операції є логічне значення true або false . У Erlang є наступні операції: == (рівно), /= (не рівно), =< (менше чи одно), < (менше), > (більше), >= (більше чи одно), і навіть порівняння, які працюють без приведення до одного типу: =/= (не дорівнює точності) і =:= (рівно точності)

Можна порівнювати значення різних типів, але вони вважаються в Erlang впорядкованими наступним чином:

число < атом < посилання < функція < порт < ідентифікатор процесу < кортеж < список < бінарні дані

Списки вважаються впорядкованими в лексикографічному порядку, а кортежі порівнюються за довжиною, і лише потім у лексикографічному порядку.

Змінні[ред. | ред. код]

Змінні служать зберігання значень простих і складових типів. Ім'я змінної починається з великої літери (у спеціальних випадках - з підкреслення) і може містити букви, цифри, підкреслення. Значення можна привласнити змінної лише один раз - ця властивість мови програмування називається одиничним присвоєнням (англ. single assignment ). До переваг одиничного присвоєння можна віднести усунення необхідності в блокуваннях, а також спрощення налагодження програми

Передача параметрів у функцію відбувається за значенням, тому всі обчислюються перед викликом функції.

Область видимості змінної поширюється від її появи в заголовної частини опису функції чи присвоювання остаточно частини опису функції. Приклад  :

binomial ( X ) - > Y = X * X , X + Y.
prod ([ 1 | T ]) -> prod ( T );
prod ([ Y | T ]) -> Y * prod ( T );
prod ([]) -> 1 .

У цьому прикладі область видимості X - весь опис функції binomial/1, а Y - від присвоєння до кінця опису. Змінна Y у другій частині (клоза) опису функції prod/1 не має відношення до змінної Y з binomial/1 : її область видимості поширюється до кінця цього клоза.

При виході обчислень за межі області видимості змінної, пам'ять, зайнята її вмістом, може бути звільнена у процесі складання сміття, якщо значення змінної не використовується в іншій частині програми.

Зіставлення зі зразком[ред. | ред. код]

Зіставлення із зразком використовується в Erlang для присвоєння (у тому числі, при роботі з параметрами функцій), управління потоком виконання програми, отримання значень складових типів, вибору повідомлення з черги. У лівій частині порівняння (або в заголовку функції) можуть бути пов'язані (вже мають значення) і незв'язані (отримуючі значення) змінні, а також літерали (атоми, числа, рядки). В результаті виконання порівняння може виявитися успішним (у цьому випадку змінні зв'язуються зі значеннями) та неуспішним – змінні залишаються непов'язаними. У зразку можуть бути змінні, значення яких для зразка байдуже: їх імена записуються з підкреслення . Змінна з ім'ям _ (підкреслення) зіставляється з будь-яким значенням, але при цьому немає зв'язування. Таку змінну можна використовувати багато разів.

Функції[ред. | ред. код]

Програми Erlang складаються з функцій, які викликають одне одного. Кількість параметрів функції називається арністю . При виклику функції заголовки описи функції зіставляються зі зразком. У разі збігу параметрів виклику формальні параметри зв'язуються з фактичними і виконується відповідна частина тіла функції. Запис варіанта обчислення функції для деякого зразка може називатися клозом від англ. clause, а визначення функції - це набір з одного або більше клозів.

Для уточнення зіставлення зі зразком у функціях можна використовувати охоронні вирази, які випливають після ключового слова when . У прикладі нижче визначено функцію обчислення знака числа, яка розраховується в залежності від порівняння параметра з нулем:

sign(X) when X > 0 -> 1;
sign(X) when X == 0 -> 0;
sign(X) when X < 0 -> -1.

Клози Erlang перебирає в тому порядку, в якому вони записані, доки не буде знайдений відповідний заголовок. В охоронних виразах можна використовувати лише обмежений набір вбудованих функцій, оскільки ці функції не повинні мати побічних ефектів

Функції Erlang підтримують рекурсивні виклики. У разі коли визначення функції закінчується рекурсивним викликом ( хвостова рекурсія ), Erlang використовує оптимізацію: стек викликів не застосовується .

Як параметром, і результатом функції може бути інша функція. У наступному прикладі функція одного аргументу повертає функцію додавання аргументу

 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:

-module(fact).
-export([fac/1]).

fac(0) -> 1;
fac(N) when N > 0, is_integer(N) -> N * fac(N-1).

Алгоритм сортування, що нагадує швидке сортування

-module(qsort).
-export([qsort/1]).

qsort([]) -> []; % Тривіальный випадок пустого списку
qsort([Pivot|Rest]) ->
  % Конкатенація списку елементів до Pivot, списку з одного елемента Pivot и після Pivot
  qsort([Front || Front <- Rest, Front < Pivot])
  ++ [Pivot] ++
  qsort([Back || Back <- Rest, Back >= Pivot]).


У цьому прикладі функція qsort викликається рекурсивно до вичерпання всіх елементів. Вираз [Front || Front <- Rest, Front < Pivot] збирає список Front з елементів Rest таких, що елемент Front менший за Pivot . Оператор ++ склеює списки.

Умовні вирази[ред. | ред. код]

Крім вибору опису у визначенні функції, у Erlang є й інші умовні вирази: case-вирази (вираз вибору) та if-вирази. Вираз вибору дозволяє організувати зіставлення із зразком усередині функції і зазвичай має наступний синтаксис:

case вираз -вибору of
  зразок1 when охорона1 -> вираз11, вираз12, ...;
  зразок2 when охорона2 -> вираз21, вираз22, ...;
  ...
  зразокN when охоронаN -> виразN1, виразN2, ...
end

Цей вираз завжди повертає значення, що відповідає останньому обчисленому виразу в рядку з зразком, що підійшов. Це значення, що повертається може служити значенням функції, що повертається, а може бути присвоєно змінній. Як і в заголовній частині функції, після зразка може бути охоронний вираз Спрощеним варіантом case-виразу є if-вираз:

if
 охорона1 -> вираз11, вираз12, ...;
 охорона2 -> вираз21, вираз22, ...;
 ...
 охоронаN -> виразN1, виразN2, ...
end

Тут охорона1— охоронний вираз. Перший справжній охоронний вираз викликає виконання відповідних виразів, останній з яких і є значенням всього if-вираз.

Слід зауважити, що і тут в охоронному виразі можна застосовувати лише обмежений набір операцій та вбудованих функцій

Коми в охоронному вираженні працюють як операція and, наприклад:

if
  X =< 0 -> 'менше рівне нулю';
  X > 0, X < 10 -> 'більше нуля і менше десяти';
  X >= 10 -> 'більше чи дорівнює десяти';
end

Компілятор Erlang стежить за безпекою зв'язування змінних усередині умовних виразів, як видно з наступного прикладу модуля:

-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-вирази.

Препроцесор та макроси[ред. | ред. код]

Препроцесор Erlang (EPP) дозволяє вкладати файли з вихідним кодом один в інший, визначати макроси та здійснювати прості та параметризовані макропідстановки. Макрос визначається за допомогою директиви -define, а макропідстановка здійснюється вказівкою імені макросу та можливих параметрів після знака питання ( ? ). Наступний приклад показує визначення та застосування параметризованого макросу:

-define(ZERO(X),X == 0)
is_zero(T) when ?ZERO(X) -> true;
is_zero(T) -> false.

Ім'я макросу зазвичай пишеться великими літерами. Визначення макросу має містити лексеми Erlang цілком (наприклад, спроба задати частину імені змінної за допомогою макросу викликає синтаксичну помилку). Макроси можуть використовуватися для підвищення зручності читання коду в охоронних виразах, для операторів налагодження і т. п. Препроцесор має кілька визначених макросів, які не можна перевизначити: ? MODULE ? MODULE_STRING, ? FILE ? LINE ? MACHINE

Заголовний файл (розширення .hrl ) з визначеннями макросів та записів можна включити за допомогою директиви -include.

Обробка помилок[ред. | ред. код]

Для обробки виняткових ситуацій Erlang можна застосовувати конструкцію try-catch, в загальному випадку записується в наступному вигляді

try вираз для обчислення of
 зразок1 when охорона1 -> вираз1;
 зразок2 when охорона2 -> вираз2;
 ...
 зразокN when охоронаN -> виразN
catch
 класс1:зразокИскл1 when охоронаИскл1 -> виразИскл1;
 ...
 классM:зразокИсклM when охоронаИсклM -> виразИсклM;
end

Як і у випадку case-виразу, вираз, що обчислюється, зіставляється зі зразком (частини між of і catch ) для отримання результату. Після ключового слова catch слідують частини обробки винятків, у яких на додаток до зразків винятків можуть бути вказані класи винятків (перед двокрапкою): error, throw та exit . Підкреслення може використовуватись як у зразку, так і в класі виключення. Наступний простий приклад ілюструє перехоплення помилки класу error при обчисленні квадратного кореня.

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

Для створення винятків, визначених користувачем, використовується функція throw/1, яка приймає кортеж з більш детальним описом помилки, що виникла і генерує виняток класу throw . Використання цієї функції небажане через погіршення зручності читання коду програми, але може знадобитися в деяких випадках при роботі з вкладеними структурами даних, наприклад, при розборі XML . Винятки класу exit виникають у результаті виклику вбудованої функції exit/1 або сигналу виходу.

Див. також[ред. | ред. код]

Виноски[ред. | ред. код]

Посилання[ред. | ред. код]