Scott Tiger Tech Blog

Blog technologiczny firmy Scott Tiger S.A.

Archiwum: 'Języki programowania' Kategorie

Elixir

Autor: Piotr Karpiuk o 3. marca 2020

Otwartoźródłowy język programowania Elixir można, mocno upraszczając, potraktować jak zestaw makr tworzących lukier syntaktyczny nad składnią Erlanga (uwaga: chodzi o makra w stylu Lispa działające na drzewie składni AST, a nie makra tekstowe jak w C). Program Elixira może korzystać z bibliotek Erlanga i jest kompilowany do bajtkodu wykonywanego na maszynie wirtualnej BEAM Erlanga. Wszystko co da się zrobić w Erlangu, można wykonać w Eliksirze i na odwrót. W pewnym sensie Elixir ma się do Erlanga tak jak Kotlin/Scala/Groovy/Clojure do Javy/JVM czy F# do C#/.NET.

Elixir czyni programowanie w Erlangu znacznie bardziej przystępnym dla przeciętnego programisty. W związku z tym pytanie dlaczego ktoś miałby chcieć programować w Eliksirze na dobrą sprawę sprowadza się do pytania dlaczego w dzisiejszych czasach ktoś mógłby chcieć używać technologii z lat 80-tych ubiegłego tysiąclecia, takiej jak Erlang.

Cóż, okazuje się że czasy Internetu, serwisów społecznościowych i chmur obliczeniowych bardzo sprzyjają rozpowszechnieniu Erlanga. W czasach gdy ten język powstawał, w powszechnym użyciu królowały aplikacje desktopowe z interfejsem użytkownika, takie jak MS Excel czy MS Word. Erlang jako język programowania współbieżnego będący w stanie elegancko wykorzystać wiele rdzeni procesora i klastry maszyn miał zastosowanie początkowo w zasadzie tylko w telekomunikacji (firma Ericsson). Dziś zapotrzebowanie na stabilne, wydajne i skalowalne usługi serwerowe będące w stanie obsługiwać miliony połączeń klienckich jednocześnie jest już powszechne. Stąd też znajdziemy Erlanga w serwerach WhatsAppa, rozproszonej bazie danych Riak, usłudze chmurowej Heroku, systemie automatyzacji Chef, kolejce komunikatów RabbitMQ czy w systemach finansowych i serwerach gier sieciowych, ale też w aplikacjach biurowych czy systemach wbudowanych.

Spełnianie wyśrubowanych wymogów standardów telekomunikacyjnych i długi czas dojrzewania technologii przekładają się na cechy funkcjonalne bardzo pożądane na dzisiejszym rynku:

  • Krótki czas reakcji niezależnie od liczby podłączonych klientów. Na jednej maszynie wirtualnej Erlanga działa zwykle jeden proces OS, w jego ramach zaś kilka wątków OS i potencjalnie nawet miliony tzw. procesów Erlanga (zwanych dalej eprocesami, można je przyrównać do gorutyn w języku Go). Eprocesy nie współdzielą pamięci RAM i porozumiewają się między sobą za pomocą asynchronicznych komunikatów. Dzięki temu sprzątanie pamięci po takich obiektach nie jest czasochłonne i nie wymaga wstrzymywania całego systemu (jeden rdzeń CPU może błyskawicznie przeprowadzić operację czyszczenia pamięci gdy tymczasem pozostałe są zajęte standardowym przetwarzaniem). Operacje I/O są wewnętrznie delegowane do osobnych wątków lub – jeżeli jest dostępna – do odpowiedniej usługi w systemie operacyjnym; w rezultacie żaden eproces oczekujący na zakończenie operacji I/O nie blokuje wykonania innych eprocesów.
  • Błąd w obsłudze jednego połączenia ma minimalny wpływ na cały system. Znowu, pomaga w tym architektura całkowitej niezależności eprocesów od siebie.
  • Aktualizacja w czasie rzeczywistym. Z założenia taki system nie powinien przestawać działać nigdy, nawet aktualizacja oprogramowania czy rozszerzanie klastra o dodatkowe maszyny nie powinna zrywać istniejących połączeń klienckich a tym bardziej wymuszać restart systemu
  • Skalowalność i rozproszenie. Ponieważ jedynym sposobem na komunikację międzyprocesową są komunikaty asynchroniczne, nie istnieją złożone mechanizmy takie jak blokady, czy semafory, co upraszcza programowanie. Co więcej, komunikacja między eprocesami zachodzi tak samo niezależnie od tego, czy eprocesy działają na tej samej instancji maszyny wirtualnej BEAM, czy na dwóch różnych maszynach. Bardzo ułatwia to rozpraszanie systemu w klastrze maszyn.

Erlang to nie tylko język programowania, ale rozbudowana platforma programistyczna, w skład której wchodzą

  • Język,
  • Maszyna wirtualna,
  • Framework OTP (Open Telecom Platform) – udostępnia abstrakcje na typowe zadania takie jak wzorce współbieżności i rozproszenia, wykrywanie i usuwanie błędów w systemach współbieżnych, pakowanie kodu do bibliotek, wdrażanie systemów czy aktualizacja w czasie rzeczywistym
  • Narzędzia, np. do kompilowania kodu, tworzenia paczek do wdrożeń, obsługi interaktywnej powłoki itp.

Zarówno Erlang, jak i Elixir są językami cały czas wspieranymi a kod źródłowy jest dostępny na GitHubie. Procesem produkcji Erlanga nadal zarządza firma Ericsson i wypuszcza regularnie nową wersję języka co roku.

Wady

Programy Erlanga są wykonywane na maszynie wirtualnej BEAM, przez co nie są w stanie osiągnąć prędkości języków kompilowanych maszynowo, takich jak C/C++. Celem platformy nie jest jednak wyciśnięcie jak największej liczby żądań na sekundę, ale utrzymanie wydajności na przewidywanym poziomie (np. system nie spowalnia wyraźnie po włączeniu się odśmiecania pamięci). Erlang idealnie nadaje się do tworzenia systemów odpornych na błędy, które mogą działać bardzo długo bez żadnych przestojów. Jest to zarazem duże wyzwanie i aspekt, na którym skupia się platforma Erlanga.

Kolejną wadą jest ilość bibliotek, których jest znacząco mniej niż w wielu współczesnych językach programowania. W GitHubie jest blisko 5 mln projektów Javy, ale ledwie 20 tys. projektów Erlanga i ok. 40 tys. Eliksira. Powoduje to, że możesz spędzić więcej czasu nad czymś, co w przypadku innych języków trwa kilka minut. Można się spotkać z żartobliwym powiedzeniem, że Erlang czyni rzeczy trudne prostymi i komplikuje rzeczy proste.

Literatura

Elixir w akcji, Saša Jurić, 2020

Napisany w Języki programowania | Brak komentarzy »

Sztuczki i triki w języku JavaScript

Autor: Piotr Karpiuk o 21. lipca 2016

Poniżej kilka znanych mi idiomów ułatwiających programowanie w języku JavaScript. Prośba do czytelników o podawanie w komentarzach własnych propozycji.

+"15" // 15

Ale uwaga, +"15px" zwróci NaN, podczas gdy parseInt("15px") zwróci 15.


+new Date() // 1479390755672

Zamiana daty na liczbę milisekund od 1970-01-01 00:00.000.


typeof cokolwiek; // undefined

Tutaj nie wystąpi błąd, pomimo użycia niezadeklarowanej zmiennej.


var arr = Array.prototype.slice.call( arguments )
var arr = Array.from( arguments ) // ES6

Standardowy mechanizm zamiany przypominającej tablicę zmiennej arguments (parametry aktualne wywołania funkcji) na prawdziwą tablicę JavaScript.

Czytaj więcej »

Napisany w JavaScript | Brak komentarzy »

Dziwactwa JavaScriptu

Autor: Piotr Karpiuk o 18. lipca 2016

Programując w JavaScripcie i czytając różne książki na temat tego języka, natknąłem się na szereg mało intuicyjnych i dość zabawnych zachowań interpretera. Poniżej moja lista – jeśli ktoś ma inne propozycje, proszę o komentarz.

[1,3,20].sort() // [1,20,3]

Wyjaśnienie: Domyślny komparator sortuje leksykograficznie.


var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";

a == c; // true
b == c; // true
a == b; // false

Wyjaśnienie: Zwracaj szczególną uwagę na reguły porównywania za pomocą operatorów == i ===, jeżeli porównujesz dwie wartości typów innych niż proste, takie jak obiekty (typ object, łącznie z function i array). Ponieważ wartości te są w rzeczywistości przechowywane przez odniesienie, więc oba operatory == i === po prostu sprawdzają, czy odniesienia są takie same; nie sprawdzają faktycznych wartości wskazywanych przez te odniesienia. Z kolei gdy porównujemy typ prosty (np. łańcuch) z obiektem (np. tablicą), tablica jest konwertowana do łańcucha przez po prostu połączenie przecinkami (,) wszystkich wartości.


Czytaj więcej »

Napisany w JavaScript | Brak komentarzy »

Narzędzia dewelopera w Google Chrome

Autor: Piotr Karpiuk o 5. lipca 2016

Dzisiejszy artykuł będzie narzędziach, jakie przeglądarka Google Chrome udostępnia programistom do debugowania i monitorowania aplikacji webowych, pod przyciskiem F12. Dla tych co mają dużo czasu, pełny opis możliwości jest dostępny na stronach Google’a: https://developers.google.com/web/tools/chrome-devtools/. Tutaj zakładam że Czytelnik ma już jakieś pojęcie o tych narzędziach, zapewne już z nich korzysta w jakimś zakresie i chciałby możliwie szybko poszerzyć swoją wiedzę o różne przydatne i ciekawe triki, które pojawiły się ostatnimi czasy, lub były od dawna ale nie są oczywiste.

Task Manager

Kombinacja klawiszy Shift+ESC otwiera managera zadań, w którym możemy zobaczyć zużycie zasobów komputera z rozbiciem na poszczególne zakładki przeglądarki:

Edycja drzewa DOM

Czytaj więcej »

Napisany w Google Chrome, JavaScript, Nowoczesne przeglądarki WWW, WWW | Brak komentarzy »

JavaScript: Obietnice (ang. Promises)

Autor: Piotr Karpiuk o 26. maja 2014

JavaScript jest zasadniczo jednowątkowy, a wszelkie operacje I/O są wykonywane asynchronicznie. W rezultacie nasz kod wymagający kontaktu ze światem zewnętrznym staje się skomplikowany, zwłaszcza gdy uwzględnimy jeszcze obsługę błędów. Zawarty w specyfikacji EcmaScript 6 mechanizm obietnic (zaimplementowany w najnowszej wersji wiodących przeglądarek) pozwala pisać kod asynchroniczny w stylu bardziej synchronicznym.

Obietnica (ang. promise) w postaci obiektu klasy Promise reprezentuje wynik operacji asynchronicznej (wartość zwracaną w przypadku sukcesu lub błąd w razie porażki), który może nie być osiągalny na razie, ale z czasem będzie.

Obietnice możemy składać (ang. chain) ze sobą wykonując zestaw operacji asynchronicznych jedna za drugą (szeregowo) lub równolegle — w tym drugim przypadku będziemy mogli podjąć jakąś aktywność po zakończeniu wszystkich zleconych operacji. W takich łańcuchach obietnic błędy są propagowane, więc możemy na samym końcu łańcucha zdefiniować funkcję przechwytującą wszystkie możliwe błędy.

Wyobraźmy sobie kod wyświetlający powieść, przy czym poszczególne rozdziały ładowane są osobno po kolei, a najpierw pobieramy plik z URLami rozdziałów:

try {
  var story = getJSONSync('story.json');
  addHtmlToPage(story.heading);

  story.chapterUrls.forEach(function(chapterUrl) {
    var chapter = getJSONSync(chapterUrl);
    addHtmlToPage(chapter.html);
  });

  addTextToPage("All done");
}
catch (err) {
  addTextToPage("Argh, broken: " + err.message);
}

document.querySelector('.spinner').style.display = 'none';

Wszystko wygląda tu pięknie z dokładnością do tego, że synchroniczne operacje I/O blokują całą przeglądarkę. Implementacja tego z wykorzystaniem asynchronicznego I/O bardzo by nam ten kod zagmatwała.

Dzięki użyciu obietnic asynchroniczny kod może wyglądać następująco:

getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);
  return Promise.all(
    story.chapterUrls.map(getJSON)
  );
}).then(function(chapters) {
  chapters.forEach(function(chapter) {
    addHtmlToPage(chapter.html);
  });
  addTextToPage("All done");
}).catch(function(err) {
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
});

Dodatkowym zyskiem widocznym na powyższym listingu (Promise.all()) jest równoczesne wykonywanie się asynchronicznych operacji wejścia/wyjścia pobierania rozdziałów. Gdy wszystkie zostaną załadowane (funkcja then()), możemy je wyświetlić. Błędy są wychwytywane za pomocą funkcji catch().

Przydatne artykuły:

Napisany w HTML5, JavaScript | Brak komentarzy »