Ерланг в Рисоваска, част 1

Виртуална машина и възли

Като начало бих искал да изясня, че програмите, написани на Erlang, се изпълняват само във виртуална машина, която от гледна точка на Erlang се нарича възел. Има версии на виртуалната машина (или Erlang / OTP) за повечето операционни системи (Windows, Linux, Mac OS, FreeBSD, прочетох, че Erlang дори е стартиран на iPhone). Тъй като изходният код на C е отворен, компилирането за всяка операционна система не е проблем. Множество възли могат да работят на една и съща машина, въпреки че това рядко е необходимо. Всеки възел трябва да има собствено уникално име, за да може да комуникира с други възли на други компютри в мрежата. Ако не се очаква взаимодействие с други възли в мрежата, тогава възелът може да няма име. Името на възела има следния формат: „име @ IP или име на компютър“. Например, пример за локална мрежа: [email protected]. Има и компилатор за Erlang програми в собствения код на процесора: HiPE (високопроизводителен компилатор на собствен код). Той е част от Erlang/OTP. HiPE дава най-голямо ускорение при работа с двоични данни (почти десетократно) и с аритметика с плаваща запетая, в други случаи увеличението на скоростта е незначително.

Променливи, на които може да се присвои стойност само веднъж

Какво е толкова готино в Erlang, което другите езици нямат и какво разбива мозъците на програмистите в традиционните езици за програмиране (C, Delphi, Basic и т.н.)?

В Erlang има променливи, но след като им бъде присвоена стойност, тя не може да бъде променена. Какви са тези променливи, ще кажете? Да, това все още са променливи, така че имат две състояния: необвързани (стойността все още не е присвоена) и обвързана (присвоена стойност). И това е направенов езика не е случайно. Първо, той значително опростява събирането на боклук за виртуалната машина (няма нужда да следите указатели). Второ, улеснява писането на алгоритми, които са разделени на много отделни процеси, без страх, че един процес ще повреди данните на друг, тъй като те не споделят нищо помежду си.

Но как мога да реализирам, например, цикъл, ако стойността на променливата не може да бъде променена? Рекурсия! Ето най-простият пример на Erlang за повишаване на число X на степен N:

повишаване (1, _X, резултат) -> резултат; повдигане (N, X, резултат) -> повдигане (N-1, X, Резултат*X).

Компактен и красив. Например raising(3, 10, 10) ще върне 1000. Между другото, можете също да напишете raising(2000, 2, 2). Ще видите много голямо число, но изчисленията ще вървят без проблеми. Друго интересно свойство на Erlang е скрито тук: той няма стриктно въвеждане на променливи, но повече за това по-късно.

Рекурсията е лоша, ще кажете! Но не и в Erlang, ако рекурсията е написана като рекурсия на опашката, т.е. когато нищо друго не се изпълнява, след като функцията се извика сама. Примерът по-горе е само пример за рекурсия на опашката: няма нищо след рейз (N-1, X, Amount*X). И тогава виртуалната машина не трябва да помни извикванията на функции в стека. Тя веднага ги забравя, така че работи много бързо и няма ограничение за броя на гнездовете.

Всичко, което работи на възел, е или отделен процес, или част от друг процес. Процесите могат да създават други процеси и да се наблюдават взаимно. Всеки процес има свой собствен уникален номер, наречен PID. Освен това, което е много важно, PID е уникален не само на един възел, но и на всички възли в мрежата! Процесът може да открие своя собствен PID чрез извикване на функцията self(). Ето пример за изпълнение на нашата функция като отделен процес:

Всеки процес освен PID може да получи уникално име:

След това е удобно да се обърнете към такъв процес по име от всеки възел в мрежата, без да знаете неговия PID.

Отново може да възникне въпросът, че всичко работи бавно. Е, аз не! Първо, процесите в Erlang нямат нищо общо с процесите на операционната система (Erlang има собствен планировчик на процеси). На типична средна машина, простите процеси се изпълняват с около 350 000 в секунда. Може би процесите изяждат много памет? Изобщо не много: от 4kb до най-простия процес. Освен това процесите могат да бъдат поставени в режим на заспиване (хибернация) веднага след стартиране, след което можете да намалите размера на паметта като цяло до 1 kb на процес.

За да комуникират помежду си, процесите могат да изпращат съобщения един на друг:

Освен това синтаксисът не се променя, независимо дали изпращате съобщение до процес на същия възел или на друг компютър в мрежата!

Ето как съобщенията се получават от процеса:

получи -> Пид ! Здравейте; Друго съобщение -> Io:format(“Получих някакво странно съобщение:

n”, [Друго съобщение]) край.

Съгласете се, кодът за изпращане и получаване на съобщения е много компактен. В горния пример процесът ще чака завинаги, докато получи съобщение. Това може да се избегне чрез добавяне на блок „след N“ към конструкцията за получаване, където N е броят милисекунди за изчакване за получаване на съобщение.

Основни функции

На всяка променлива в Erlang може да бъде присвоена стойност от произволен тип. Но можете също да проверите какъв тип е стойността в дадена променлива, като използвате вградените функции (is_integer, is_binary и т.н.). Обикновено липсата на силно писане на даден език се счита за недостатък, но на практика установих, че по-често това е предимство и значително увеличава гъвкавостта на програмата. Освен това, за да се избегнепотенциални грешки с типове, Erlang включва статичен анализатор Dialyzer, който открива такива грешки.

Надзиратели

Сега нека да преминем към наистина интересните характеристики на средата за разработка Erlang/OTP. Програмите, написани на Erlang, обикновено се считат не просто за надеждни, но за свръхнадеждни. Как работи? Всичко е просто. Цялото приложение, написано на Erlang, най-просто казано, е разделено на супервайзори (супервайзорът е специален процес, който следи дъщерните процеси) и работни процеси, които изпълняват основната работа. Можете да прочетете повече за това в Принципите на проектиране на OTP. Има дори такава забавна концепция в Erlang: „оставете процеса да умре“. И наистина е така. Ако даден процес е наблюдаван, тогава ако се срине, той ще бъде рестартиран от надзора. Като цяло супервайзорът може гъвкаво да конфигурира поведението си в случай на срив на дъщерен процес. Например, може да спре всички дъщерни процеси в такава ситуация и да ги стартира отначало. Това е необходимо в ситуация, в която дъщерните процеси по някакъв начин зависят един от друг и падането на един може да доведе до неработоспособност на останалите.

По този начин една програма, изградена на принципите на OTP, ще изглежда като дърво от процеси:Фигурата е взета от принципите на OTP Design

Като се има предвид, че надзорниците не извършват никакви изчисления, а само наблюдават, вероятността от падането им клони към нула. Разбира се, целият възел може да се срине, например поради липса на памет, но и тук има решение: можете да стартирате специален процес на операционната система, който следи възела и го рестартира след определено време. Има и друга опция: разпределени приложения (Distributed Applications) - това са приложения, които могат да работят на няколковъзли. Освен това в даден момент приложението работи само на един възел. В случай на срив на възела, на който работи такова приложение, то автоматично се рестартира на следващия възел в списъка. Списъкът с възли, където може да се изпълнява разпределено приложение, може да се променя динамично по време на работа.

Работа с двоични данни

Erlang има наистина бърза работа с двоични данни (особено във връзка с HiPE), така че е много естествено да записвате обработка на входни двоични данни върху него, до работа с отделни битове. Тук, в сравнение с Java 6, Erlang печели няколко пъти по отношение на скоростта.

недостатъци

Статията няма да е пълна, ако не споменем основните недостатъци на Erlang:

  • липса на поддръжка за Unicode низове (изтъркана липса на език, въпреки че обещават да добавят поддръжката си към Erlang R13B тази пролет; ние чакаме),
  • бавна математика, писането на някои сериозни математически изчисления върху нея е неефективно,
  • малък брой допълнителни библиотеки (въпреки че всички основни неща са налице и броят на библиотеките непрекъснато расте, все още е далеч от такова разнообразие като в .NET или C),
  • липса на дебъгвана и бърза графична библиотека, така че не бих писал клиентски приложения в Erlang (разбира се, има например wxErlang, но библиотеката все още е далеч от пълната).
Но всички тези недостатъци, освен може би липсата на поддръжка за Unicode низове, могат или да бъдат заобиколени, или всъщност не са недостатъци, тъй като този език не е създаден за това. И е създаден зависоконатоварени, мащабируеми, ултранадежднисистеми. В самото начало езикът е създаден специално за използване в телекомуникациите, но както се оказа по-късно, той е идеален за създаване наУеб сървъри и разпределени системи.

Следва продължение