Erlang

Η Erlang είναι μια γλώσσα προγραμματισμού γενικών καθηκόντων με χαρακτηριστικά ταυτοχρονισμού (concurrency) και συλλογής απορριμμάτων. Το ίδιο όνομα αναφέρεται και στο σύστημα χρόνου εκτέλεσής της (runtime system). Το υποσύνολο της Erlang που μπορεί να εκτελείται ακολουθιακά είναι μια γλώσσα συναρτησιακού προγραμματισμού, με αυστηρή αποτίμηση (strict evaluation), μοναδική ανάθεση (single assignment) και δυναμικό σύστημα τύπων (dynamic typing). Όσον αφορά τον ταυτοχρονισμό, ακολουθεί το μοντέλο Actor. Αναπτύχθηκε στην Ericsson για την υποστήριξη κατανεμημένων, ανθεκτικών σε σφάλματα εφαρμογών που να εκτελούνται σε πραγματικό χρόνο (soft-real-time) και χωρίς διακοπή. Υποστηρίζει άμεση ενημέρωση του κώδικα κατά την εκτέλεση (hot swapping), χωρίς να χρειάζεται να σταματήσει το σύστημα.
Αν και στις περισσότερες γλώσσες προγραμματισμού η χρήση νημάτων (threads) θεωρείται δύσκολη και επιρρεπής σε λάθη, η Erlang παρέχει χαρακτηριστικά στο επίπεδο της ίδιας της γλώσσας για τη δημιουργία και το χειρισμό διεργασιών (processes), ώστε να διευκολύνεται η ανάπτυξη ταυτόχρονου κώδικα. Αν και όλος ο ταυτοχρονισμός είναι ρητός στην Erlang, οι διεργασίες επικοινωνούν μεταξύ τους περνώντας μηνύματα αντί μέσω κοινών μεταβλητών, χωρίς να χρειάζονται κλειδώματα (locks).
Η πρώτη έκδοση αναπτύχθηκε από το Τζο Άρμστρονγκ το 1986. Η γλώσσα αρχικά ήταν ιδιόκτητο λογισμικό της Ericsson, αλλά διανεμήθηκε σαν λογισμικό ανοιχτού κώδικα το 1998.

Ιστορία

Το όνομα "Erlang" αποδίδεται στον Bjarne Däcker και θεωρείται ότι είτε αναφέρεται στο Δανό μαθηματικό και μηχανικό Agner Krarup Erlang, ή ότι σημαίνει "Ericsson Language" ("γλώσσα της Ericsson").
Η Erlang σχεδιάστηκε για καλύτερη ανάπτυξη εφαρμογών τηλεφωνίας. Η αρχική της έκδοση υλοποιήθηκε σε Prolog.
Το 1998 ανακοινώθηκε το σύστημα Ericsson AXD301 (τύπου switch), που περιλάμβανε πάνω από ένα εκατομμύριο γραμμές Erlang, και ανακοινώθηκε ότι η αξιοπιστία του ήταν 99.9999999%. Λίγο αργότερα η Erlang απαγορεύτηκε στα Ericsson Radio Systems για νέα προϊόντα, λόγω προτίμησης σε μη-ιδιόκτητες γλώσσες. Η απαγόρευση αυτή ήταν η αιτία που ο Άρμστρονγκ και άλλοι έφυγαν από την Ericsson. Η υλοποίηση έγινε λογισμικό ανοιχτού κώδικα το τέλος του ίδιου χρόνου. Τελικά η απαγόρευση σταμάτησε να ισχύει στην Ericsson και ο Άρμστρονγκ επαναπροσλήφθηκε στην Ericsson το 2004.
Το 2006 προστέθηκε υποστήριξη για συμμετρικές πολυδιεργασίες (symmetric multiprocessing) στο σύστημα χρόνου εκτέλεσης και στην εικονική μηχανή (virtual machine).

Παραδείγματα συναρτησιακού προγραμματισμού

Ο αλγόριθμος παραγοντικό σε Erlang:
-module(fact).    % Επειδή είναι το αρχείο 'fact.erl', το όνομα της μονάδας (module) και του αρχείου ΠΡΕΠΕΙ να είναι ίδια-export([fac/1]). % Εξάγει τη συνάρτηση 'fac' με αριθμό ορισμάτων ίσο με 1 (1 παράμετρος, χωρίς τύπο ή όνομα)fac(0) -> 1; % Αν 0, επιστρέφει 1, αλλιώς (το σύμβολο ; σημαίνει 'αλλιώς')fac(N) when N > 0, is_integer(N) -> N * fac(N-1).% Ορίζεται αναδρομικά και επιστρέφει το αποτέλεσμα% (η τελεία . σημαίνει 'τέλος της if'/'endif' ή 'τέλος της συνάρτησης')
Ένας αλγόριθμος ταξινόμησης (παρόμοιος με την quicksort):
%% qsort:qsort(List)%% Ταξινομεί μια λίστα από αντικείμενα-module(qsort).     % Είναι το αρχείο 'qsort.erl'-export([qsort/1]). % Εξάγεται μια συνάρτηση 'qsort' με 1 παράμετρο (χωρίς τύπο ή όνομα)qsort([]) -> []; % Αν είναι η κενή λίστα [], επιστρέφει μια κενή λίστα (δεν υπάρχει κάτι προς ταξινόμηση)qsort([Pivot|Rest]) ->
    % Συνθέτει αναδρομικά μια λίστα 'Front' όλα τα στοιχεία που πρέπει να είναι πριν το 'Pivot',
    % μετά το 'Pivot' και μετά τη λίστα 'Back' με όλα τα στοιχεία που πρέπει να είναι μετά το 'Pivot'
    qsort([Front || Front <- Rest, Front < Pivot])
    ++ [Pivot] ++
    qsort([Back || Back <- Rest, Back >= Pivot]).
Το παραπάνω παράδειγμα καλεί αναδρομικά τη συνάρτηση qsort μέχρι να μη μείνει κάτι προς ταξινόμηση. Η έκφραση [Front || Front <- Rest, Front < Pivot] ονομάζεται list comprehension, που σημαίνει “Κατασκεύασε μια λίστα από στοιχεία Front τέτοια ώστε το Front να ανήκει στη Rest και να είναι μικρότερο από το Pivot.” Ο τελεστής ++ συνενώνει λίστες.
Μια συνάρτηση σύγκρισης μπορεί να χρησιμοποιηθεί για πιο πολύπλοκες δομές, για να είναι πιο ευανάγνωστος ο κώδικας.
Ο παρακάτω κώδικας ταξινομεί λίστες ανάλογα με το μήκος τους:
% Είναι το αρχείο 'listsort.erl'-module(listsort).% Εξάγει τη 'by_length' με 1 παράμετρο (δεν έχει σημασία ο τύπος ή το όνομα)-export([by_length/1]).by_length(Lists) -> % Χρησιμοποιεί την 'qsort/2' και δίνει μια ανώνυμη συνάρτηση σαν παράμετρο
   qsort(Lists, fun(A,B) when is_list(A), is_list(B) -> length(A) < length(B) end).qsort([], _)-> []; % Αν η λίστα είναι άδεια, επιστρέφει μια κενή λίστα (αγνοεί τη δεύτερη παράμετρο)qsort([Pivot|Rest], Smaller) ->
    % Χωρίζει τη λίστα σε στοιχεία 'Smaller' πριν το 'Pivot' και υπόλοιπα (μη-'Smaller')
    % μετά από το 'Pivot' και ταξινομεί τις υπολίστες.
    qsort([X || X <- Rest, Smaller(X,Pivot)], Smaller)
    ++ [Pivot] ++
    qsort([Y ||Y <- Rest, not(Smaller(Y, Pivot))], Smaller).
Η Pivot εδώ θεωρείται ότι είναι η πρώτη παράμετρος που δίνεται στην qsort() και το υπόλοιπο της Lists ονομάζεται Rest. Η έκφραση:
[X || X <- Rest, Smaller(X,Pivot)]
δε διαφέρει σε μορφή από την:
[Front || Front <- Rest, Front < Pivot]
(στο προηγούμενο παράδειγμα) εκτός από τη χρήση μιας συνάρτησης σύγκρισης στο τελευταίο μέρος, που λέει “Κατασκεύασε μια λίστα από στοιχεία X τέτοια ώστε το X να ανήκει στη Rest και το Smaller να είναι αληθές", με το Smaller να έχει οριστεί νωρίτερα σαν:
fun(A,B) when is_list(A), is_list(B) -> length(A) < length(B) end
Η ανώνυμη συνάρτηση ονομάζεται Smaller στη λίστα παραμέτρων του δεύτερου ορισμού της qsort και μπορεί να κατονομαστεί με αυτόν τον τρόπο μέσα στη συνάρτηση. Δεν ονομάζεται στον πρώτο ορισμό της qsort, που χειρίζεται τη βασική περίπτωση της κενής λίστας και δε χρειάζεται τη συνάρτηση, άρα ούτε κάποιο όνομα για αυτήν.

Ταυτοχρονισμός και κατανεμημένος προγραμματισμός

Το βασικό πλεονέκτημα της Erlang είναι η υποστήριξη ταυτοχρονισμού (concurrency). Η γλώσσα περιλαμβάνει λίγες αλλά ισχυρές βασικές εντολές με τις οποίες μπορούν να δημιουργηθούν διεργασίες και να επικοινωνούν μεταξύ τους. Οι διαδικασίες είναι ο βασικός τρόπος δόμησης ενός προγράμματος σε Erlang. Οι διεργασίες της Erlang σε γενικές γραμμές ακολουθούν το μοντέλο Communicating Sequential Processes (CSP). Δεν είναι διεργασίες του λειτουργικού συστήματος, ούτε νήματα, αλλά ελαφρές διεργασίες, παρόμοιες με τα "πράσινα νήματα" που είχε αρχικά η Java. Όπως οι διεργασίες του λειτουργικού συστήματος, και σε αντίθεση με τα πράσινα νήματα και τα νήματα του λειτουργικού συστήματος, οι διεργασίες της Erlang δεν έχουν κοινό χώρο μεταξύ τους. Το κόστος τους επίσης είναι χαμηλό, το ελάχιστο κόστος υπολογίζεται σε 300 λέξεις (words), επομένως μπορούν να δημιουργηθούν πολλές διεργασίες χωρίς πρόβλημα στην ταχύτητα του προγράματος: έχουν δοκιμαστεί καταστάσεις με 20 εκατομμύρια διεργασίες στην πράξη. Η Erlang υποστηρίζει συμμετρική πολυδιεργασία από την έκδοση R11B του Μαΐου του 2006.
Η επικοινωνία ανάμεσα στις διεργασίες (inter-process communication) γίνεται μέσω ενός ασύγχρονου συστήματος περάσματος μηνυμάτων, χωρίς κοινό χώρο μεταξύ των διεργασιών ("shared-nothing"): κάθε διεργασία έχει ένα "γραμματοκιβώτιο" (“mailbox”), που είναι μια ουρά από μηνύματα που έχουν σταλεί από άλλες διεργασίες και δεν έχουν καταναλωθεί ακόμα. Μια διεργασία χρησιμοποιεί τη βασική εντολή receive για να διαβάζει μηνύματα που συμφωνούν με κάποιους κανόνες (patterns). Μια ρουτίνα χειρισμού μηνυμάτων ελέγχει όλα τα μηνύματα αν ταιριάζουν με κάθε κανόνα, μέχρι να βρει κάποιο. Όταν το μήνυμα καταναλωθεί και αφαιρεθεί από το γραμματοκιβώτιο, η διεργασία συνεχίζει την εκτέλεσή της. Ένα μήνυμα μπορεί να περιέχει οποιαδήποτε δομή της 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("Ήρθε μήνυμα hello: ~s", [Text]);
         {goodbye, Text} -> io:format("Ήρθε μήνυμα goodbye: ~s", [Text])
 end.
Όπως δείχνει το παράδειγμα, μια διεργασία μπορεί να δημιουργηθεί σε κάποιο απομακρυσμένο μηχάνημα και η επικοινωνία με αυτή είναι "διαφανής", δηλαδή λειτουργεί ακριβώς όπως η επικοινωνία με μια τοπική διεργασία.
Ο ταυτοχρονισμός υποστηρίζεται από το μηχανισμό χειρισμού λαθών της Erlang. Όταν μια διεργασία καταρρεύσει για κάποιο λόγο, τερματίζει και στέλνει ένα μήνυμα στη διεργασία που την ελέγχει, για να την ενημερώσει. Αυτός ο τρόπος χειρισμού λαθών κάνει ευκολότερη τη συντήρηση του λογισμικού και κάνει τον κώδικα λιγότερο πολύπλοκο.

Υλοποίηση

Η υλοποίηση της Erlang της Ericsson φορτώνει κώδικα byte (bytecode) στην εικονική μηχανή της που μετατρέπεται σε threaded code στο χρόνο φόρτωσης. Περιέχει επίσης και ένα μεταγλωττιστή σε κώδικα μηχανής για πολλές πλατφόρμες, που έχει αναπτυχθεί από το High Performance Erlang Project (HiPE) του Πανεπιστημίου της Ουψάλα. Από τον Οκτώβριο του 2001 το σύστημα HiPE έχει ενσωματωθεί πλήρως στο σύστημα ανοιχτού κώδικα της Ericsson (Open Source Erlang/OTP).  Υποστηρίζει επίσης διερμηνεία, απευθείας από τον πηγαίο κώδικα, μέσω ενός αφηρημένου συντακτικού δένδρου (abstract syntax tree), από την έκδοση R11B-5.

Φόρτωση κώδικα και μονάδες κώδικα

Ο κώδικας φορτώνεται σαν "μονάδες" κώδικα ("modules") και το σύστημα μπορεί να κρατά δύο εκδόσεις μιας μονάδας στη μνήμη ταυτόχρονα, ενώ οι διεργασίες μπορούν να εκτελούν κώδικα ταυτόχρονα και από τις δύο. Οι εκδόσεις τότε αναφέρονται σαν η "παλιά" και η "νέα" έκδοση - μια διεργασία μετακινείται σε μια νέα έκδοση όταν κάνει κλήση στη μονάδα της.
Ακολουθεί ένα παράδειγμα αυτού του μηχανισμού (που ονομάζεται "hot code loading"):
  %% Μια διεργασία που κρατά ένα μετρητή.
  %% Πρώτη έκδοση
  -module(counter).
  -export([start/0, codeswitch/1]).

  start() -> loop(0).

  loop(Sum) ->
    receive
       {increment, Count} ->
          loop(Sum+Count);
       {counter, Pid} ->
          Pid ! {counter, Sum},
          loop(Sum);
       code_switch ->
          ?MODULE:codeswitch(Sum)
          % Ζητά να χρησιμοποιήσει την 'codeswitch/1' από την τελευταία έκδοση του MODULE
    end.

  codeswitch(Sum) -> loop(Sum).
Στη δεύτερη έκδοση, προστίθεται η δυνατότητα επαναφοράς του μετρητή στο μηδέν.
  %% Δεύτερη έκδοση
  -module(counter).
  -export([start/0, codeswitch/1]).

  start() -> loop(0).

  loop(Sum) ->
    receive
       {increment, Count} ->
          loop(Sum+Count);
       reset ->
          loop(0);
       {counter, Pid} ->
          Pid ! {counter, Sum},
          loop(Sum);
       code_switch ->
          ?MODULE:codeswitch(Sum)
    end.

  codeswitch(Sum) -> loop(Sum).
Ο παραπάνω κώδικας που εκτελείται επαναλαμβανόμενα θα καλέσει εξωτερικά την codeswitch/1 (η ?MODULE είναι μακροεντολή του προεπεξεργαστή για την τρέχουσα μονάδα κώδικα) μόνο όταν λάβει ένα μήνυμα που να αποτελείται από το άτομο 'code_switch'. Αν υπάρχει μια νέα έκδοση της μονάδας "counter" στη μνήμη, τότε θα κληθεί η συνάρτηση codeswitch/1 που της ανήκει. Η ύπαρξη ενός συγκεκριμένου σημείου εισόδου σε μια νέα έκδοση είναι πρακτική που επιτρέπει στον προγραμματιστή να μετατρέψει την κατάσταση του προγράμματος όπως απαιτείται από τη νέα έκδοση. Στο παράδειγμα, η κατάσταση είναι ένας ακέραιος.
Στην πράξη τα συστήματα κατασκευάζονται με βάση αρχές σχεδίασης από το Open Telecom Platform, που οδηγούν σε δυνατότητες καλύτερης αναβάθμισης του κώδικα. Η επιτυχής φόρτωση με αυτόν τον τρόπο είναι γενικά δύσκολη - ο κώδικας πρέπει να ξαναγράφεται για να χρησιμοποιεί τις δυνατότητες της Erlang.

Διανομή

Το 1998, η Ericsson διένειμε την Erlang σαν λογισμικό ανοιχτού κώδικα για να εξασφαλίσει ότι θα είναι ανεξάρτητη κατασκευαστή, καθώς και για να την κάνει πιο γνωστή. Η Erlang, μαζί με βιβλιοθήκες και την κατανεμημένη βάση δεδομένων πραγματικού χρόνου Mnesia, αποτελούν τη συλλογή βιβλιοθηκών Open Telecom Platform (OTP). Η Ericsson και κάποιες ακόμα εταιρείες προσφέρουν εμπορική υποστήριξη για την Erlang.
Από τη διάθεση του ανοιχτού κώδικα, η Erlang έχει χρησιμοποιηθεί από πολλές εταιρείες διεθνώς, όπως η Norte και η T-Mobile. Αν και η Erlang σχεδιάστηκε για συγκεκριμένες ανάγκες και για μεγάλο μέρος της ιστορίας της δεν ήταν γνωστή, γίνεται όλο και δημοφιλέστερη λόγω της ζήτησης για ταυτόχρονες υπηρείες.
Η Erlang χρησιμοποιείται σε εξυπηρετητές MMORPG.
Η Erlang είναι διαθέσιμη για πολλά λειτουργικά συστήματα τύπου Unix, όπως το Mac OS X, αλλά και για τα Microsoft Windows.