Erlang (linguagem de programação) - Erlang (programming language)

Erlang
Erlang logo.svg
Paradigmas Multi-paradigma : concorrente , funcional
Projetado por
Desenvolvedor Ericsson
Apareceu pela primeira vez 1986 ; 35 anos atrás ( 1986 )
Versão estável
24.0.2  Edite isso no Wikidata / 1 de junho de 2021 ; há 4 meses ( 1 de junho de 2021 )
Disciplina de digitação Dinâmico , forte
Licença Licença Apache 2.0
Extensões de nome de arquivo .erl, .hrl
Local na rede Internet www .erlang .org
Implementações principais
Erlang
Influenciado por
Lisp , PLEX , Prolog , Smalltalk
Influenciado
Akka , Clojure , Dart , Elixir , F # , Opa , Oz , Reia , Rust , Scala , Go

Erlang ( / ɜr l æ ŋ / UR -lang ) é um de uso geral , simultâneo , funcional linguagem de programação , e uma coleta de lixo sistema de execução . O termo Erlang é usado alternadamente com Erlang / OTP, ou Open Telecom Platform (OTP), que consiste no sistema de tempo de execução Erlang , vários componentes prontos para uso (OTP) escritos principalmente em Erlang e um conjunto de princípios de design para Erlang programas.

O sistema de tempo de execução Erlang é projetado para sistemas com estas características:

A linguagem de programação Erlang possui dados imutáveis , correspondência de padrões e programação funcional . O subconjunto sequencial da linguagem Erlang oferece suporte para avaliação rápida , atribuição única e digitação dinâmica .

Um aplicativo Erlang normal é construído a partir de centenas de pequenos processos Erlang.

Era originalmente um software proprietário da Ericsson , desenvolvido por Joe Armstrong , Robert Virding e Mike Williams em 1986, mas foi lançado como software livre e de código aberto em 1998. Erlang / OTP é suportado e mantido pela Open Telecom Platform (OTP) unidade de produto da Ericsson .

História

O nome Erlang , atribuído a Bjarne Däcker, foi presumido por aqueles que trabalham nos comutadores de telefonia (para os quais a linguagem foi projetada) como uma referência ao matemático e engenheiro dinamarquês Agner Krarup Erlang e uma abreviatura silábica de "Ericsson Language". Erlang foi projetado com o objetivo de aprimorar o desenvolvimento de aplicações de telefonia. A versão inicial do Erlang foi implementada no Prolog e foi influenciada pela linguagem de programação PLEX usada nas primeiras trocas da Ericsson. Em 1988, Erlang provou que era adequado para protótipos de centrais telefônicas, mas o intérprete do Prolog era lento demais. Um grupo da Ericsson estimou que seria necessário ser 40 vezes mais rápido para ser adequado para uso em produção. Em 1992, o trabalho começou na máquina virtual BEAM (VM) que compila Erlang para C usando uma mistura de código compilado nativamente e código encadeado para encontrar um equilíbrio entre desempenho e espaço em disco. De acordo com Armstrong, a linguagem passou de produto de laboratório para aplicações reais após o colapso da central telefônica AX de próxima geração chamada AX-N em 1995. Como resultado, Erlang foi escolhido para a próxima central de modo de transferência assíncrona (ATM) AXD .

Em 1998, a Ericsson anunciou o switch AXD301, contendo mais de um milhão de linhas de Erlang e relatou ter alcançado uma alta disponibilidade de nove "9" s . Pouco tempo depois, a Ericsson Radio Systems proibiu o uso interno do Erlang para novos produtos, citando a preferência por linguagens não proprietárias. A proibição fez com que Armstrong e outros deixassem a Ericsson. A implementação foi open-source no final do ano. A Ericsson acabou suspendendo a proibição e recontratou Armstrong em 2004.

Em 2006, o suporte a multiprocessamento simétrico nativo foi adicionado ao sistema de tempo de execução e VM.

Processos

Os aplicativos Erlang são construídos a partir de processos Erlang muito leves no sistema de tempo de execução Erlang. Os processos Erlang podem ser vistos como objetos "vivos" ( programação orientada a objetos ), com encapsulamento de dados e passagem de mensagens , mas capazes de mudar o comportamento durante o tempo de execução. O sistema de tempo de execução Erlang fornece isolamento de processo estrito entre processos Erlang (isso inclui coleta de dados e lixo, separados individualmente por cada processo Erlang) e comunicação transparente entre processos em nós Erlang diferentes (em hosts diferentes).

Joe Armstrong, co-inventor de Erlang, resumiu os princípios dos processos em sua tese de doutorado :

  • Tudo é um processo.
  • Os processos são fortemente isolados.
  • A criação e destruição do processo é uma operação leve.
  • A passagem de mensagens é a única maneira de os processos interagirem.
  • Os processos têm nomes exclusivos.
  • Se você souber o nome de um processo, pode enviar uma mensagem para ele.
  • Os processos não compartilham recursos.
  • O tratamento de erros não é local.
  • Os processos fazem o que deveriam fazer ou falham.

Joe Armstrong comentou em uma entrevista à Rackspace em 2013: "Se Java é ' escreva uma vez, execute em qualquer lugar ', então Erlang é 'escreva uma vez, execute para sempre'.”

Uso

Em 2014, a Ericsson informou que Erlang estava sendo usado em seus nós de suporte e em redes móveis GPRS , 3G e LTE em todo o mundo e também pela Nortel e T-Mobile .

Como Tim Bray , diretor de tecnologias da Web da Sun Microsystems , expressou em sua palestra na O'Reilly Open Source Convention (OSCON) em julho de 2008:

Se alguém viesse a mim e quisesse me pagar muito dinheiro para construir um sistema de gerenciamento de mensagens em grande escala que realmente precisava estar ativo o tempo todo, nunca poderia deixar de funcionar por anos seguidos, eu sem hesitar escolheria Erlang para construí-lo.

Erlang é a linguagem de programação usada para codificar o WhatsApp .

Desde que foi lançado como código aberto, Erlang se espalhou além das telecomunicações, estabelecendo-se em outros setores como FinTech, Gaming, Healthcare, Automotive, IoT e Blockchain. Além do WhatsApp, há outras empresas listadas como casos de sucesso de Erlang: Vocalink (uma empresa MasterCard), Goldman Sachs , Nintendo , AdRoll, Grindr , BT Mobile , Samsung , OpenX , SITA .

Exemplos de programação funcional

Fatorial

Um algoritmo fatorial implementado em Erlang:

-module(fact). % This is the file 'fact.erl', the module and the filename must match
-export([fac/1]). % This exports the function 'fac' of arity 1 (1 parameter, no type, no name)

fac(0) -> 1; % If 0, then return 1, otherwise (note the semicolon ; meaning 'else')
fac(N) when N > 0, is_integer(N) -> N * fac(N-1).
% Recursively determine, then return the result
% (note the period . meaning 'endif' or 'function end')
%% This function will crash if anything other than a nonnegative integer is given.
%% It illustrates the "Let it crash" philosophy of Erlang.

Sequência de Fibonacci

Um algoritmo recursivo final que produz a sequência de Fibonacci :

%% The module declaration must match the file name "series.erl" 
-module(series).

%% The export statement contains a list of all those functions that form
%% the module's public API.  In this case, this module exposes a single
%% function called fib that takes 1 argument (I.E. has an arity of 1)
%% The general syntax for -export is a list containing the name and
%% arity of each public function
-export([fib/1]).

%% ---------------------------------------------------------------------
%% Public API
%% ---------------------------------------------------------------------

%% Handle cases in which fib/1 receives specific values
%% The order in which these function signatures are declared is a vital
%% part of this module's functionality

%% If fib/1 is passed precisely the integer 0, then return 0
fib(0) -> 0;

%% If fib/1 receives a negative number, then return the atom err_neg_val
%% Normally, such defensive coding is discouraged due to Erlang's 'Let
%% it Crash' philosophy; however, in this case we should explicitly
%% prevent a situation that will crash Erlang's runtime engine
fib(N) when N < 0 -> err_neg_val;

%% If fib/1 is passed an integer less than 3, then return 1
%% The preceding two function signatures handle all cases where N < 1,
%% so this function signature handles cases where N = 1 or N = 2
fib(N) when N < 3 -> 1;

%% For all other values, call the private function fib_int/3 to perform
%% the calculation
fib(N) -> fib_int(N, 0, 1).

%% ---------------------------------------------------------------------
%% Private API
%% ---------------------------------------------------------------------

%% If fib_int/3 receives a 1 as its first argument, then we're done, so
%% return the value in argument B.  Since we are not interested in the
%% value of the second argument, we denote this using _ to indicate a
%% "don't care" value
fib_int(1, _, B) -> B;

%% For all other argument combinations, recursively call fib_int/3
%% where each call does the following:
%%  - decrement counter N
%%  - Take the previous fibonacci value in argument B and pass it as
%%    argument A
%%  - Calculate the value of the current fibonacci number and pass it
%%    as argument B
fib_int(N, A, B) -> fib_int(N-1, B, A+B).

Aqui está o mesmo programa sem os comentários explicativos:

-module(series).
-export([fib/1]).

fib(0) -> 0;
fib(N) when N < 0 -> err_neg_val;
fib(N) when N < 3 -> 1;
fib(N) -> fib_int(N, 0, 1).

fib_int(1, _, B) -> B;
fib_int(N, A, B) -> fib_int(N-1, B, A+B).

Ordenação rápida

Quicksort em Erlang, usando compreensão de lista :

%% qsort:qsort(List)
%% Sort a list of items
-module(qsort).     % This is the file 'qsort.erl'
-export([qsort/1]). % A function 'qsort' with 1 parameter is exported (no type, no name)

qsort([]) -> []; % If the list [] is empty, return an empty list (nothing to sort)
qsort([Pivot|Rest]) ->
    % Compose recursively a list with 'Front' for all elements that should be before 'Pivot'
    % then 'Pivot' then 'Back' for all elements that should be after 'Pivot'
    qsort([Front || Front <- Rest, Front < Pivot]) ++ 
    [Pivot] ++
    qsort([Back || Back <- Rest, Back >= Pivot]).

O exemplo acima invoca recursivamente a função qsortaté que não haja mais nada para ser classificado. A expressão [Front || Front <- Rest, Front < Pivot]é uma compreensão de lista , que significa "Construir uma lista de elementos Fronttal que Frontseja membro de Reste Frontseja menor que Pivot." ++é o operador de concatenação da lista.

Uma função de comparação pode ser usada para estruturas mais complicadas por uma questão de legibilidade.

O código a seguir classificaria as listas de acordo com o comprimento:

% This is file 'listsort.erl' (the compiler is made this way)
-module(listsort).
% Export 'by_length' with 1 parameter (don't care about the type and name)
-export([by_length/1]).

by_length(Lists) -> % Use 'qsort/2' and provides an anonymous function as a parameter
   qsort(Lists, fun(A,B) -> length(A) < length(B) end).

qsort([], _)-> []; % If list is empty, return an empty list (ignore the second parameter)
qsort([Pivot|Rest], Smaller) ->
    % Partition list with 'Smaller' elements in front of 'Pivot' and not-'Smaller' elements
    % after 'Pivot' and sort the sublists.
    qsort([X || X <- Rest, Smaller(X,Pivot)], Smaller)
    ++ [Pivot] ++
    qsort([Y || Y <- Rest, not(Smaller(Y, Pivot))], Smaller).

A Pivoté obtido do primeiro parâmetro fornecido a qsort()e o resto de Listsé nomeado Rest. Observe que a expressão

[X || X <- Rest, Smaller(X,Pivot)]

não é diferente na forma de

[Front || Front <- Rest, Front < Pivot]

(no exemplo anterior) exceto para o uso de uma função de comparação na última parte, dizendo "Construa uma lista de elementos Xtal que Xseja um membro de Rest, e Smallerseja verdadeiro", Smallersendo definido anteriormente como

fun(A,B) -> length(A) < length(B) end

A função anônima é nomeada Smallerna lista de parâmetros da segunda definição de qsortpara que possa ser referenciada por esse nome dentro dessa função. Não é nomeado na primeira definição de qsort, que trata do caso base de uma lista vazia e, portanto, não tem necessidade dessa função, muito menos de um nome para ela.

Tipos de dados

Erlang tem oito tipos de dados primitivos :

Inteiros
Os inteiros são escritos como sequências de dígitos decimais, por exemplo, 12, 12375 e -23427 são inteiros. A aritmética de inteiros é exata e limitada apenas pela memória disponível na máquina. (Isso é chamado de aritmética de precisão arbitrária .)
Átomos
Os átomos são usados ​​em um programa para denotar valores distintos. Eles são escritos como cadeias de caracteres alfanuméricos consecutivos, sendo o primeiro caractere minúsculo. Os átomos podem conter qualquer caractere se estiverem entre aspas simples e existir uma convenção de escape que permite que qualquer caractere seja usado dentro de um átomo. Os átomos nunca são coletados como lixo e devem ser usados ​​com cuidado, especialmente se estiver usando a geração dinâmica de átomos.
Flutuadores
Os números de ponto flutuante usam a representação IEEE 754 de 64 bits .
Referências
As referências são símbolos globalmente únicos cuja única propriedade é que podem ser comparados para igualdade. Eles são criados avaliando o primitivo Erlang make_ref().
Binários
Um binário é uma sequência de bytes. Os binários fornecem uma maneira eficiente de armazenamento de dados binários. As primitivas Erlang existem para compor e decompor binários e para entrada / saída eficiente de binários.
Pids
Pid é a abreviação de identificador de processo  - um Pid é criado pela primitiva Erlang. spawn(...)Pids são referências a processos Erlang.
Ports
As portas são usadas para se comunicar com o mundo externo. As portas são criadas com a função integrada open_port. As mensagens podem ser enviadas e recebidas de portas, mas essas mensagens devem obedecer ao chamado "protocolo de porta".
Funs
Funs são encerramentos de funções . Funs são criados por expressões da forma: fun(...) -> ... end.

E três tipos de dados compostos:

Tuplas
Tuplas são contêineres para um número fixo de tipos de dados Erlang. A sintaxe {D1,D2,...,Dn}denota uma tupla cujos argumentos são D1, D2, ... Dn.Os argumentos podem ser tipos de dados primitivos ou tipos de dados compostos. Qualquer elemento de uma tupla pode ser acessado em tempo constante.
Listas
Listas são contêineres para um número variável de tipos de dados Erlang. A sintaxe [Dh|Dt]denota uma lista cujo primeiro elemento é Dhe cujos elementos restantes são a lista Dt. A sintaxe []denota uma lista vazia. A sintaxe [D1,D2,..,Dn]é abreviação de [D1|[D2|..|[Dn|[]]]]. O primeiro elemento de uma lista pode ser acessado em tempo constante. O primeiro elemento de uma lista é chamado de cabeça da lista. O restante de uma lista quando a cabeça foi removida é chamada a cauda da lista.
Mapas
Os mapas contêm um número variável de associações de valores-chave. A sintaxe é #{Key1=>Value1,...,KeyN=>ValueN}.

Duas formas de açúcar sintático são fornecidas:

Cordas
As strings são escritas como listas de caracteres duplamente citadas. Este é um açúcar sintático para uma lista de pontos de código Unicode inteiros para os caracteres na string. Assim, por exemplo, a string "gato" é uma abreviação de [99,97,116].
Registros
Os registros fornecem uma maneira conveniente de associar uma tag a cada um dos elementos em uma tupla. Isso permite que se refira a um elemento de uma tupla por nome e não por posição. Um pré-compilador pega a definição do registro e a substitui pela referência de tupla apropriada.

Erlang não tem nenhum método para definir classes, embora existam bibliotecas externas disponíveis.

Estilo de codificação "Deixe travar"

Erlang é projetado com um mecanismo que torna mais fácil para processos externos monitorarem travamentos (ou falhas de hardware), ao invés de um mecanismo em processo como tratamento de exceção usado em muitas outras linguagens de programação. As falhas são relatadas como outras mensagens, que é a única maneira de os processos se comunicarem entre si, e os subprocessos podem ser gerados de forma barata. A filosofia "deixe travar" prefere que um processo seja completamente reiniciado em vez de tentar se recuperar de uma falha grave. Embora ainda exija o tratamento de erros, essa filosofia resulta em menos código dedicado à programação defensiva, onde o código de tratamento de erros é altamente contextual e específico.

Árvores de supervisor

Um aplicativo Erlang típico é escrito na forma de uma árvore de supervisor. Essa arquitetura é baseada em uma hierarquia de processos em que o processo de nível superior é conhecido como "supervisor". O supervisor então gera vários processos filho que agem como trabalhadores ou mais, supervisores de nível inferior. Essas hierarquias podem existir em profundidades arbitrárias e provaram fornecer um ambiente altamente escalonável e tolerante a falhas dentro do qual a funcionalidade do aplicativo pode ser implementada.

Dentro de uma árvore de supervisor, todos os processos de supervisor são responsáveis ​​por gerenciar o ciclo de vida de seus processos filho, e isso inclui lidar com situações em que esses processos filho falham. Qualquer processo pode se tornar um supervisor, primeiro gerando um processo filho e, em seguida, chamando erlang:monitor/2esse processo. Se o processo monitorado travar, o supervisor receberá uma mensagem contendo uma tupla cujo primeiro membro é o átomo 'DOWN'. O supervisor é responsável, em primeiro lugar, por ouvir essas mensagens e, em segundo lugar, por tomar as medidas adequadas para corrigir a condição de erro.

Concorrência e orientação de distribuição

A principal força do Erlang é o suporte para simultaneidade . Ele tem um pequeno, mas poderoso conjunto de primitivas para criar processos e se comunicar entre eles. Erlang é conceitualmente semelhante à linguagem occam , embora reformule as idéias de processos sequenciais de comunicação (CSP) em uma estrutura funcional e use a passagem de mensagem assíncrona. Os processos são o meio principal de estruturar um aplicativo Erlang. Eles não são processos do sistema operacional nem threads , mas processos leves que são agendados pelo BEAM. Como os processos do sistema operacional (mas ao contrário dos threads do sistema operacional), eles não compartilham nenhum estado entre si. A sobrecarga mínima estimada para cada um é de 300 palavras . Assim, muitos processos podem ser criados sem degradar o desempenho. Em 2005, um benchmark com 20 milhões de processos foi executado com sucesso com Erlang de 64 bits em uma máquina com 16 GB de memória de acesso aleatório (RAM; total de 800 bytes / processo). Erlang oferece suporte ao multiprocessamento simétrico desde o lançamento R11B de maio de 2006.

Enquanto threads precisam de suporte de biblioteca externa na maioria das linguagens, Erlang fornece recursos de nível de linguagem para criar e gerenciar processos com o objetivo de simplificar a programação simultânea. Embora toda a simultaneidade seja explícita em Erlang, os processos se comunicam usando a passagem de mensagens em vez de variáveis ​​compartilhadas, o que elimina a necessidade de bloqueios explícitos (um esquema de bloqueio ainda é usado internamente pela VM).

A comunicação entre processos funciona através de um sistema de passagem de mensagens assíncronas sem compartilhamento : cada processo tem uma "caixa de correio", uma fila de mensagens que foram enviadas por outros processos e ainda não foram consumidas. Um processo usa a primitiva para recuperar mensagens que correspondem aos padrões desejados. Uma rotina de tratamento de mensagens testa as mensagens em cada padrão, até que um deles corresponda. Quando a mensagem é consumida e removida da caixa de correio, o processo retoma a execução. Uma mensagem pode compreender qualquer estrutura Erlang, incluindo primitivas (inteiros, flutuantes, caracteres, átomos), tuplas, listas e funções. receive

O exemplo de código abaixo mostra o suporte integrado para processos distribuídos:

 % Create a process and invoke the function web:start_server(Port, MaxConnections)
 ServerProcess = spawn(web, start_server, [Port, MaxConnections]),

 % Create a remote process and invoke the function
 % web:start_server(Port, MaxConnections) on machine RemoteNode
 RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),

 % Send a message to ServerProcess (asynchronously). The message consists of a tuple
 % with the atom "pause" and the number "10".
 ServerProcess ! {pause, 10},

 % Receive messages sent to this process
 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.

Como mostra o exemplo, os processos podem ser criados em nós remotos e a comunicação com eles é transparente, no sentido de que a comunicação com processos remotos funciona exatamente como comunicação com processos locais.

A simultaneidade oferece suporte ao método principal de tratamento de erros em Erlang. Quando um processo falha, ele sai perfeitamente e envia uma mensagem ao processo de controle, que pode então agir, como iniciar um novo processo que assume a tarefa do processo antigo.

Implementação

A implementação de referência oficial do Erlang usa BEAM. O BEAM está incluído na distribuição oficial do Erlang, chamada Erlang / OTP. BEAM executa bytecode que é convertido em código encadeado no momento do carregamento. Também inclui um compilador de código nativo na maioria das plataformas, desenvolvido pelo High Performance Erlang Project (HiPE) na Uppsala University . Desde outubro de 2001, o sistema HiPE está totalmente integrado ao sistema Open Source Erlang / OTP da Ericsson. Ele também suporta interpretação, diretamente do código-fonte via árvore de sintaxe abstrata , via script a partir do lançamento R11B-5 do Erlang.

Carregamento de código quente e módulos

Erlang suporta atualização dinâmica de software em nível de idioma . Para implementar isso, o código é carregado e gerenciado como unidades de "módulo"; o módulo é uma unidade de compilação . O sistema pode manter duas versões de um módulo na memória ao mesmo tempo, e os processos podem executar código de cada uma simultaneamente. As versões são referidas como a versão "nova" e a "antiga". Um processo não mudará para a nova versão até que faça uma chamada externa para seu módulo.

Um exemplo do mecanismo de carregamento de código quente:

  %% A process whose only job is to keep a counter.
  %% First version
  -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)
          % Force the use of 'codeswitch/1' from the latest MODULE version
    end.

  codeswitch(Sum) -> loop(Sum).

Para a segunda versão, adicionamos a possibilidade de zerar a contagem.

  %% Second version
  -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).

Somente ao receber uma mensagem consistindo do átomo code_switch, o loop executará uma chamada externa para codeswitch / 1 ( ?MODULEé uma macro de pré-processador para o módulo atual). Se houver uma nova versão do módulo contador na memória, então sua função codeswitch / 1 será chamada. A prática de ter um ponto de entrada específico em uma nova versão permite ao programador transformar o estado para o que é necessário na versão mais recente. No exemplo, o estado é mantido como um inteiro.

Na prática, os sistemas são construídos usando princípios de design da plataforma Open Telecom, o que leva a mais designs que podem ser atualizados por código. O carregamento de código quente bem-sucedido é exigente. O código deve ser escrito com cuidado para fazer uso das instalações de Erlang.

Distribuição

Em 1998, a Ericsson lançou o Erlang como um software gratuito e de código aberto para garantir sua independência de um único fornecedor e aumentar o conhecimento do idioma. Erlang, junto com as bibliotecas e o banco de dados distribuído em tempo real Mnesia , forma a coleção de bibliotecas OTP. A Ericsson e algumas outras empresas oferecem suporte comercial à Erlang.

Desde o lançamento do código aberto, Erlang tem sido usado por várias empresas em todo o mundo, incluindo Nortel e T-Mobile . Embora Erlang tenha sido projetado para preencher um nicho e tenha permanecido uma linguagem obscura durante a maior parte de sua existência, sua popularidade está crescendo devido à demanda por serviços simultâneos. Erlang encontrou alguma utilidade em distribuir jogos de RPG online para múltiplos jogadores (MMORPG).

Veja também

Referências

Leitura adicional

links externos