Erlang (linguagem de programação)

linguagem de programação Da Wikipédia, a enciclopédia livre

Erlang (linguagem de programação)

Erlang (/ˈɜrlæŋ/ UR-lang) é uma linguagem de programação de propósito geral, de alto nível, funcional e concorrente.[3][4] Seu sistema de tempo de execução (runtime) é especificamente arquitetado para o desenvolvimento de sistemas escaláveis, distribuídos, tolerantes a falhas, de tempo real suave, de alta disponibilidade e uptime.[3] Erlang é compilada para bytecode e interpretada pela máquina virtual BEAM, executada junto ao Erlang Run-Time System (ERTS) — responsável por interfacear o código com o sistema operacional, providenciando gerenciamento dinâmico de memória através de um coletor de lixo, entrada e saída de dados, escalonamento de processos, comunicação em rede, integração com sistema de arquivos, dentre demais recursos. Seu runtime suporta ainda hot swapping — alteração do código da aplicação sob execução de forma ininterrupta — para conveniência em ambientes de alta disponibilidade e tempo real, onde elevado uptime se faz necessário.[5]

Factos rápidos
Erlang
Thumb
Thumb
Ambiente shell do Erlang (Eshell) exibindo um programa que calcula o fatorial de cada um dos números da sequência de 1 a 25 de forma concorrente através de processos leves.
Paradigma
Surgido em 1986 (3839 anos)
Última versão 27.3 (5 de março de 2025; há 33 dias[1])
Criado por Ericsson
Estilo de tipagem
Projetado por
  • Joe Armstrong
  • Robert Virding
  • Mike Williams
Principais implementações
  • Erlang/OTP
  • AtomVM
  • Firefly
Influenciada por
Influenciou
Sistema operacional
Licença Apache 2.0
Extensão do arquivo .erl
.hrl
Página oficial www.erlang.org
Fechar

Erlang é uma linguagem de programação funcional, de tipagem dinâmica e forte, com dados imutáveis, funções de primeira classe, funções de alta ordem e suporte a casamento de padrões (pattern matching). A imutabilidade de dados possibilita que estados de execução do programa sejam revertidos em casos de falhas[3] e previne a ocorrência de condições de corrida e deadlocks em contextos de concorrência, bem como fornecem maior integridade e facilidade na paralelização de operações computacionais ao eliminar a necessidade da presença de mecanismos de sincronização de estado compartilhado e exclusão mútua como locks ou semáforos.[4][6][7]

Uma aplicação Erlang é tipicamente composta por dezenas, centenas ou milhares de processos leves (light-weight processes) — unidades de computação multitarefa executadas pela máquina virtual e gerenciadas pelo runtime.[3][4][8] Diferentemente dos habituais processos ou threads do sistema operacional, os processos leves presentes em Erlang são arquitetados para serem facilmente e rapidamente criados, inicializados, reinicializados ou finalizados; são executados de forma isolada dos demais processos existentes; alocam por padrão pouca quantidade de memória e são escalonados para execução de forma automática pelo runtime.[3][4][8] Processos comunicam-se entre si de forma assíncrona através de troca de mensagens, que em Erlang são denominadas sinais (signals).[3][8] Processos podem ainda ser gerenciados por um processo supervisor, responsável por monitorá-los e reinicializá-los em caso de falhas ou conforme outras políticas de execução.[3][4][9] Supervisores podem ser monitorados por outros supervisores de forma hierarquizada, formando dessa forma árvores de supervisão que conferem maior robustez e resiliência para a execução da aplicação.[4][9] A plataforma OTP fornece ainda supervisores capazes de monitorar uso de recursos como CPU, armazenamento em massa e memória de processos filhos.[10]

Erlang/OTP (Open Telecom Plataform), escrita na linguagem de programação C[11], é a principal implementação de facto da linguagem e do runtime da linguagem Erlang, cuja concepção e desenvolvimento iniciou-se em 1986 no Computer Science Laboratory da multinacional sueca Ericsson através de uma equipe formada pelos engenheiros e cientistas da computação Joe Armstrong, Robert Virding, e Mike Williams.[12] Inicialmente desenvolvida de forma fechada e proprietária por mais de uma década, a plataforma Erlang/OTP foi publicada em dezembro de 1998 como software livre e de código aberto.[12] Dentre outras implementações de código aberto da máquina virtual e do runtime Erlang, destacam-se ainda a AtomVM, escrita em C para microcontroladores e demais dispositivos embarcados[13][14], bem como a Firefly — escrita em Rust para WebAssembly.[15]

Sistemas e aplicações escritas em Erlang tem sido extensivamente utilizadas em produção em inúmeras áreas há mais de três décadas. Erlang/OTP é uma peça fundamental na infraestrutura de telecomunicações GPRS, 3G, 4G e 5G da Ericsson[16], sendo também a linguagem na qual é escrito o back end da rede social e plataforma de mensagens instantâneas WhatsApp[16], o agente de mensagens de código aberto RabbitMQ, o banco de dados NoSQL distribuído de código aberto Apache CouchDB, a plataforma de trading de fundos de cobertura do banco de investimentos e serviços financeiros multinacional americano Goldman Sachs[16], o serviço de notificações push que oferece suporte a operação online do console de jogos eletrônicos Nintendo Switch[16][17], bem como os sistemas do Serviço Nacional de Saúde (National Health Service - NHS) do Reino Unido[16] — são alguns exemplos notáveis de seu emprego na indústria.

História

Resumir
Perspectiva

Antecedentes

O desenvolvimento de Erlang foi precedido pelas pesquisas relacionadas a questões de escalabilidade, concorrência e tolerância a falhas presentes em sistemas de telecomunicações — especialmente em centrais de comutação de circuitos de telefonia — durante as décadas de 1970 e 1980, e se originou nas instalações de pesquisa da companhia Ellemtel, na Suécia. A Ellemtel era uma empresa estabelecida em maio de 1970[18] como Ellemtel Utvecklings AB e de propriedade conjunta (uma joint venture) da Ericsson e da Televerket — essa última a empresa estatal sueca de telecomunicações.[12] No início da década de 1970, a Ellemtel avançava em trabalhos de pesquisa e desenvolvimento uma SPCs (Stored Program Control) — centrais telefônicas controladas por computadores e circuitos digitais.[12]

Thumb
Uma central telefônica Ericsson AXE. O domínio de problemas de escalabilidade, concorrência e tolerância a falhas presentes em centrais de comutação de circuitos de telefonia motivaram o desenvolvimento da linguagem PLEX e influenciaram diretamente a concepção de Erlang.

Centrais telefônicas são compostas por vários dispositivos denominados switches (comutadores) individuais, que usualmente lidam com dezenas a centenas de milhares de chamadas telefônicas simultâneas. O sistema de comutação deve ser capaz de escalar para suportar uma grande quantidade de chamadas, bem como tolerar a falha de switches individuais, garantindo que as chamadas prossigam de forma ininterrupta para os usuários.[12] Usualmente, em uma SPC, o software realiza o controle de execução das chamadas telefônicas, enquanto que o hardware atua como responsável pela transmissão de dados e sinais.[12] De forma geral, na perspectiva do software que realiza o controle de chamadas, uma chamada telefônica é uma máquina de estados finito que altera seu estado em resposta a mensagens de um protocolo de comunicação.[12] O sistema necessita lidar com uma grande coleção dessas máquinas ou processos simultâneos aguardando por um evento disparado pela recepção de uma mensagem ou outros tipos de sinais. Ao receber um evento, o processo realiza uma pequena quantidade de computação, altera seu estado, possivelmente envia mensagens para outros processos e então entra em espera pelo próximo evento.[12] Processos devem ser vistos individualmente pelo sistema de forma isolada, de forma com que erros em um processo devido a uma falha de software ou hardware não ocasionem na parada total do sistema, prejudicando os demais processos.[12] Em todo caso, a quantidade de computação executa é tipicamente menor que em sistemas de propósito geral, com trocas de mensagens, alteração de estados/contextos e decodificação de mensagens de rede desempenhando papeis mais relevantes.[12][19]

Através dos esforços de pesquisa e desenvolvimento da Ellemtel, em 1976 surgiu a Ericsson AXE-10, uma central telefônica digital para comutação de circuitos.[12] A AXE era programada em PLEX (Programming Language for EXchanges), uma linguagem de programação pseudo concorrente, assíncrona, orientadas a eventos e de tempo real desenvolvida por Göran Heimdahl na Ericsson para operar sistemas de comutação de circuitos telefônicos.[12] PLEX é uma linguagem de domínio específico, de alto nível, desenvolvida especificamente para controlar centrais telefônicas, carecendo de construções comuns de linguagens de propósito geral como estruturas de repetição, suporte para valores numéricos negativos ou números reais.[20] Em vez disso, PLEX dispõe de um paradigma orientado a eventos (como quando um usuário retira o telefone de seu suporte para fazer a discagem de um número) que, codificados como sinais, ativavam a execução de rotinas de código — e somente eventos permitiam a execução de código.[20] PLEX dispõe ainda de jobs, unidades de execução de código pseudo concorrentes implementadas em hardware na AXE que podem ser escalonadas para execução periódica ou em um momento posterior.[12][20] Embora aparentemente paralelos para o programador, Jobs não são executados de forma genuinamente concorrente; em vez disso, uma vez ativados por uma rotina (que prossegue normalmente com sua execução), jobs são movidos para uma fila (queue) e são executados em ordem de entrada — e não de forma paralela (concorrente).[20]

Estima-se que o emprego de PLEX tornou o processo de programação do sistema da AXE três vezes mais rápido comparado ao emprego de linguagem de máquina, bem como reduziu o custo total do projeto em 20 a 30%.[21] O sistema contava com cerca de cinco milhões de linhas de código escritas em PLEX e era designada para manter um downtime de até três minutos anualmente.[22] A AXE tornou-se um dos principais produtos de sucesso da Ericsson durante as décadas de 1980 e 1990, sendo amplamente empregado na indústria de telecomunicações em todos os continentes.[21] Entretanto, a linguagem PLEX estava intimamente acoplada ao hardware da AXE, o que significada que ela não poderia ser portada para outros sistemas e aplicações.[12]

Concepção

Por volta de 1985, funcionários do Computer Science Laboratory (CSLab) — estabelecido no ano anterior na Ericsson — estavam desenvolvendo estudos e experimentos para determinar as características desejáveis em uma linguagem de programação para programar sistemas de telecomunicações.[22][23] Algumas linguagens disponíveis na época foram selecionadas para análise, dentre elas Concurrent Euclid, CHILL e Ada (imperativas), PFL e Prolog (declarativas), OPS4 (baseada em regras), Frames e CLU (orientadas a objetos).[23] Tendo a disposição no laboratório uma central telefônica PABX Ericsson MD110 conectada a uma rede de workstations executando Unix, a equipe escreveu diversos programas experimentais para lidar com POTS (Plain Old Telephone Service) com as linguagens selecionadas.[22][24] Eventualmente a equipe de trabalho chegou a conclusão que os programas para operar com POTS eram significativamente mais sucintos e fáceis de serem lidos quando escritos em linguagens declarativas em comparação com aqueles escritos em linguagens imperativas; todavia, as linguagens declarativas analisadas careciam de recursos para concorrência e os mecanismos de tratamento de erros disponíveis eram demasiadamente simples.[22] Além disso, os seguintes requisitos foram estabelecidos como desejáveis para programação de sistemas de telecomunicações:

  • Concorrência massiva e granular: assim como jobs em PLEX, processes em CHILL e tasks em Ada, uma linguagem destinada a sistemas de telecomunicações deveria possuir de forma explícita (embutida em si mesma) um conceito de unidade de execução concorrente ("processos"), sem depender de abstrações de baixo nível oferecidas pelo sistema operacional (como threads em C).[22][24] Idealmente, processos deveriam ser executados de forma isolada uns dos outros e interagirem somente através de troca de mensagens.[25] A granularidade permitiria adequar a concorrência a partes menores do sistema, oferecendo maior escalabilidade com menor uso de recursos.[12]
  • Trocas assíncronas de mensagens: os processos deveriam ser capazes de trocar mensagens e informações entre si através de operações assíncronas.[22] Esse conceito contraria a ideia de manter recursos e estados compartilhados (como a passagem de ponteiros de memória).[12] Devido ao isolamento, processos deveriam operar somente com os recursos que lhes foram explicitamente delegados.[12]
  • Tratamento robusto e automático de erros: deveria ser possível recuperar automaticamente erros e falhas de software e hardware que inflijam o sistema, assegurando a sua execução ininterrupta.[12] O isolamento de processos asseguraria que erros individuais em um processo não se propagassem para o restante do sistema, inviabilizando sua operação.[12] Através do tratamento automático de erros, seria possível a adoção da programação ofensiva, onde o programador oferece mais atenção ao código do domínio de negócio e menos ao código de tratamento de erros, tornando o código mais sucinto e legível.[23]
  • Operação contínua: assim como em PLEX/AXE, deveria ser possível realizar alterações no código do sistema sem que fosse necessária a sua interrupção, possibilitando manutenções sem sacrificar a continuidade dos serviços.[22]

O resultados da pesquisa foram apresentados por Bjarne Däcker, Nabiel Elshiewy, Per Hedeland, Carl Wilhelm Welin, e Mike Williams no artigo "Experiments with Programming Languages and Techniques for Telecommunications Applications" durante a conferência Software Engineering for Telecommunication Switching Systems (SETSS), ocorrida em Eindhoven, Países Baixos, entre 14 e 18 de abril de 1986.[22]

Desenvolvimento inicial

Ainda em 1986, os resultados obtidos a partir dos estudos e experimentos demonstraram que Prolog era uma linguagem potencialmente adequada para o desenvolvimento de aplicações de telefonia.[12][23] De acordo com Armstrong:

Thumb
Robert Virding (à esquerda) e Joe Armstrong (à direita), designers da linguagem de programação Erlang. Foto de 2013.

Erlang foi concebida tendo em mente um claro objetivo: providenciar uma forma melhor de se programar aplicações de telefonia.[12]

Posteriormente, Armstrong citou que inicialmente a equipe de trabalho não tinha a intenção de desenvolver uma nova linguagem; Erlang surgiu como um efeito colateral da procura por aperfeiçoar o desenvolvimento de aplicações PABX para telefonia de forma "conveniente e elegante".[26] Já tendo obtido os recursos ideais necessários a partir dos estudos e experimentações, a equipe decidiu então aperfeiçoar a linguagem Prolog, dada a sua sintaxe concisa e clara.[12][23] Armstrong inicialmente escreveu um meta-interpretador para Prolog, adicionando o conceito de processos com a capacidade de serem suspensos para a linguagem e recursos para facilitar a detecção e recuperação de erros; futuramente, no início de 1992, Armstrong, Virding e Williams publicaram o processo de trabalho no meta-interpretador em um artigo intitulado "Use of Prolog for developing a new programming language" apresentado durante uma conferência realizada em Londres sobre as aplicações práticas de Prolog.[12][26] Esse primeiro meta-interpretador, escrito em cerca de 1 100 linhas de NU-Prolog e executado através de um VAX 11/750, evoluiu para suportar os seguintes recursos:[26]

  • Processos concorrentes;
  • Trocas seletivas de mensagens;
  • Agrupamento de processos;
  • Sinalização de erros e mecanismos de trapping baseados em agrupamentos de processos;
  • Código Prolog in line;
  • Fatias de tempo (time slices) de execução;
  • Escalonamento de processos através do algoritmo Round-robin;
  • Tracing de mensagens entre processos;
  • Depuração individual de processos;
  • I/O baseada em canais virtuais (portas).

Primeiros usuários

No final de 1987, o CSLab entrou numa parceria com a Ericsson Business Communications AB (EBC) através de um time de prototipação de tecnologia de telecomunicações. Através do estudo e análise de diversos sistemas PABX (Private Branch Exchange) existentes, a equipe da EBC definiu uma arquitetura PABX aprimorada, que recebeu a designação Audial Communication System (ACS); A equipe demonstrou interesse no meta-interpretador e na nova linguagem desenvolvida pelo CSLab que, ainda em fase de prototipação, foi selecionada para desenvolver o novo sistema.[23][26] A arquitetura ACS estruturava o sistema em um sistema operacional básico (Basic Operating System - BOS), um sistema operacional de aplicações (Applications Operating System - AOS), e aplicações. O BOS contava com recursos sofisticados de supervisão do sistema que futuramente foram utilizados para compor a Open Telecom Platform (OTP).[23] Entre 1988 e 1989, a equipe colaborou intensamente no protótipo do sistema, denominado ACS/Dunder; reuniões de alinhamento entre as equipes do CSLab e da EBC eram realizadas duas vezes durante a semana.[23][26] Essa colaboração fez com que o desenvolvimento do meta-intepretador fosse estabilizado e documentado — uma vez que ele possuía agora usuários externos — além de incorporar feedbacks da base de usuários.[26] Nesse período, a nova linguagem teve sua sintaxe alterada do estilo lógico do Prolog para uma mais próxima a funcional, e o CSLab teve sucesso em convencer os programadores da EBC sobre suas vantagens para o desenvolvimento de aplicações.[23][26]

Origens do nome

Armstrong atribuiu a Bjarne Däcker a cunhagem do nome "Erlang" para a nova linguagem, embora não se saiba se o termo é uma abreviação para "Ericsson Language" ou uma referência para o matemático e engenheiro dinamarquês Agner Krarup Erlang.[12][25][27] Armstrong ainda citou que a própria equipe encorajou essa ambiguidade.[12]

Aprimoramento

Eventualmente, a carga de trabalho fora desviada do meta-interpretador para a documentação da linguagem.[26] Esse processo fora feito através de manuais e guias de referência, o que Armstrong entendia como uma sinalização do alcance de um novo nível de maturidade para a linguagem.[26]

Entretanto, o próximo passo seria aprimorar o desempenho da linguagem.[26] Embora os ganhos em eficiência e produtividade fossem notáveis e adequados para um protótipo, não eram satisfatórios o suficiente para um produto; o coletor de lixo até então necessitava que o sistema pausasse periodicamente para poder operar e reclamar a memória, o que inviabilizava seu uso em um sistema de tempo real.[23] Os usuários do sistema na EBC também desejavam que a execução fosse acelerada em até 40%.[23]

As primeiras abordagens para aprimorar o desempenho da linguagem foram escrevendo compiladores cruzados para outras linguagens a partir de Prolog. Em 1989 foram escritos compiladores de Erlang para Parlog e STRAND88.[26] STRAND88 é uma linguagem declarativa, de sintaxe semelhante a Prolog, designada para programação distribuída e paralela através de um conjunto de máquinas, sendo executada através de uma máquina virtual escrita em C. Embora a implementação para STRAND88 fosse ordens de magnitude mais rápida que o interpretador escrito em Prolog, ela ainda estava distante da performance desejável, e a equipe encontrou problemas tentando traduzir o modelo de concorrência baseada em processos para STRAND88, que operava com concorrência através de uma granularidade menor.[26]

Ainda em 1989, surgiu a ideia de tentar transferir a execução de Erlang para uma máquina abstrata de Warren (Warren abstract machine, WAM). A WAM foi desenvolvida em 1983 a partir dos trabalhos de David H. D. Warren, e descrevia uma arquitetura de memória e um conjunto de instruções específicos para a execução de Prolog, se tornando também um target padrão de facto para compiladores de Prolog.[28] Embora popular, era de conhecimento comum que muito do trabalho de Warren era obscuro e sem justificativas claras, o que tornava seu entendimento difícil e igualmente o seu processo de implementação;[26][28] com a equipe no CSLab não ocorreu diferente. Tendo o entendimento do funcionamento de uma WAM finalmente clarificados após a leitura de um livro técnico sobre o tópico, a equipe conseguiu implementar com sucesso uma máquina virtual e um compilador para ela, ambos escritos em Prolog.[26] A máquina virtual, denominada Joe's Abstract Machine (JAM), era demasiadamente lenta para qualquer aplicação real, apesar de ser útil para depurar o conjunto de instruções e auxiliar na codificação da pilha de chamadas.[26][29] Tendo a codificação da versão em Prolog tendo sido finalizada, a JAM foi então portada para a linguagem C, tendo Williams escrito o sistema de runtime em C, Armstrong escrito o compilador em Erlang (através de bootstrapping), e Virding escrito as bibliotecas para a linguagem.[26][29] Junto com Däcker, os autores posteriormente descreveram os detalhes de implementação da máquina virtual em um artigo intitulado "Implementing a functional language for highly parallel real time applications" e publicado durante a conferência SETSS 92, realizada entre 30 de março e 1 de abril de 1992, em Florença, Itália.[30]

Embora a JAM ainda não atendesse as crescentes demandas da EBC por eficiência, ela provou que uma linguagem funcional concorrente poderia ser utilizada para desenvolver sistemas de tempo real suave, onde os tempos de resposta são medidos na ordem dos milissegundos.[23]

Maturidade

Em 1990, Erlang estava sendo recomendada para propósitos de prototipação internamente na Ericsson e já era empregada em diferentes sistemas na empresa.[23] Erlang foi revelada pela primeira vez ao público por Armstrong e Virding durante a XIII IEEE International Symposium on Switching (ISS 90), que ocorreu entre 27 de maio e 1 de junho daquele ano em Älvsjö, Estocolmo. Na conferência, que contou com cerca de dois mil atendentes,[23] os autores publicaram um artigo intitulado "ERLANG - An Experimental Telephony Programming Language",[24] apresentando Erlang como sendo "uma linguagem projetada para a prototipação de robustas aplicações distribuídas concorrentes e de tempo real, livre de efeitos colaterais e contendo mecanismos para robustez, com uma noção explícita de concorrência e um mecanismo incluso de tratamento de erros"; o artigo introduzia também uma sintaxe inicial para a linguagem, organização de código-fonte em módulos, além dos mecanismos de comunicação entre processos e tratamento de erros.[24] Ainda de acordo com os autores, diversos interpretadores para Erlang escritos em Prolog já haviam sido escritos e portados para diferentes máquinas — DEC VAX 11/750, Sun 3/60 e IBM PC/AT. Esses interpretadores eram utilizados para controlar hardware experimental para o sistema PABX Ericsson MD110 conectado a uma rede de workstations Sun 3/60, onde a distribuição era realizada através de uma central telefônica Ericsson MD110 Group Switch.[24]

Em 1991, a equipe de trabalho na linguagem foi convidada para presidir um curso técnico em Erlang no Instituto Real de Tecnologia (KTH) em Estocolmo. Erlang se tornou parte integral do curso de programação de sistemas paralelos da universidade.[23]

No início de 1992, Erlang estava sendo emprega em inúmeros protótipos e aplicações menores internas e empregada em cerca de 30 instalações — quantidade que crescia em torno de uma ou duas mensalmente —, sendo também publicamente e gratuitamente distribuída para fins não comerciais.[26] Ainda nesse ano, a Ericsson decidiu tornar a linguagem num produto para ser utilizado em projetos em produção. A equipe de trabalho recebeu a permissão para escrever um livro técnico de referência para a linguagem, intitulado "Concurrent Programming in Erlang" e publicado no ano seguinte pela Prentice Hall.[26][31]

De acordo com o co-inventor Armstrong, a linguagem saiu do laboratório para aplicações reais após descontinuarem a central AXE, chamada AXE-N em 1995. Como resultado, Erlang foi escolhido para o próximo modelo chamado AXD.[25]

Em 1998, a Ericsson anunciou a central AXD301 contendo mais de um milhão de linhas de código de Erlang, com relatos de atingir confiabilidade superior a nove "9"s[32]. Logo a seguir o Erlang foi banido na Ericsson Radio System para novos produtos devido a uma preferência pelo uso de linguagens não proprietárias, e Armstrong e outros 14 funcionários deixaram seus cargos por esse motivo, e fundaram uma companhia chamada Bluetail, direcionada a utilizar a tecnologia Erlang em outros produtos. A Ericsson voltou atrás mais tarde e Armstrong foi re-admitido em 2004.[33]

Em 2006, o suporte a multiprocessamento simétrico foi adicionado na máquina virtual e no runtime.[25]

Sintaxe

Resumir
Perspectiva

Tipos primitivos

Erlang possui oito tipos de dados primitivos. Um term ou termo é qualquer valor ou símbolo pertencente a qualquer um dos tipos presentes na linguagem.[34]

Integer

Erlang suporta aritmética de inteiros exata e de precisão arbitrária (bignum) através do tipo integer(). Zeros à esquerda são ignorados e subtraços (_) podem ser inseridos entre os dígitos para facilitar sua leitura.[34] Para além da notação convencional, podem ser escritos como uma sequência iniciada pelo caractere $ e sucedida por caracteres codificados em ASCII ou code-points Unicode representando o literal numérico, ou alternativamente, através da notação base#valor onde a base é um literal inteiro entre 2 e 36, inclusos.[34]

A nível de implementação no runtime, inteiros são armazenados em memória como inteiros nativos caso possam ser representados em 1 palavra (o que equivale a 4 bytes em arquiteturas 32 bits ou 8 bytes em 64 bits).[35] Isso possibilita que tais inteiros possam caber em registradores na CPU, tornando operações aritméticas mais rápidas e eficientes. Em contraste, inteiros maiores que 1 palavra — tipicamente bignums — serão armazenados no heap do processo e referenciados através de ponteiros, o que significa que ocuparão, no mínimo, 3 palavras — 1 palavra como um ponteiro para as 2 ou mais palavras representando o inteiro alocadas no heap; operações aritméticas com tais tipos de inteiros são intrinsecamente mais lentas, dada a necessidade da CPU acessar a memória através do ponteiro para ser capaz de copiar e escrever seus dados.[35]

% Exemplo de execução na Eshell.

1> 42.
42

2> -1_234_567_890.
-1234567890

3> $A.
65

4> $\n.
10

5> 2#101.
5

6> 16#1f.
31

7> 16#4865_316F_774F_6C64.
5216630098191412324

8> 36#helloworld.
1767707668033969

Float

Erlang suporta aritmética de ponto flutuante de 64 bits através do tipo float(). O tipo float() não oferece conformidade total com o padrão IEEE 754, pois Erlang não suporta Inf (infinitos) ou NaNs (indefinidos).[34] Qualquer operação que resulte em NaN, +Inf, ou -Inf lançará uma exceção badarith por parte do runtime.[34]

% Exemplo de execução na Eshell.

1> 2.3.
2.3

2> 2.3e3.
2.3e3

3> 2.3e-3.
0.0023

4> 1_234.333_333
1234.333333

Atom

Um atom (átomo) é um literal alfanumérico constante. Caso não esteja enclausurada por apóstrofos ('), a declaração de um atom deve começar com uma letra minúscula na codificação ASCII e não deve conter espaços ou pontos (.).[34] Atoms não enclausurados estão restritos ao conjunto de caracteres ISO/IEC 8859-1 (Latin-1), enquanto que os enclausurados podem empregar caracteres Unicode.[36] Atoms podem conter individualmente até 255 caracteres, e por padrão o runtime está limitado a conter no máximo 1 048 576 atoms, embora esse limite possa ser incrementado.[37]

A nível de implementação no runtime, atoms são armazenados em uma tabela em memória e cada referência para um atom ocupa 1 palavra em memória. O coletor de lixo do runtime não desaloca a memória ocupada por atoms em desuso, portando a geração dinâmica de atoms em tempo de execução, quando necessária, deve ocorrer com cautela.[35]

hello
numero_telefonico
número_telefônico
hostname@node
'Segunda-feira'
'número telefônico'
'Wikipédia 🌎'
'wikipedia@127.0.0.1'

Bit String/Binary

Um bit strings representa uma sequência arbitrária de bits em memória.[34][38] Um bit string cujo tamanho é um múltiplo de 8 bits (um octeto) é denominado binary (binário), de modo que todo binary é um bit string, mas nem todo bit string é um binary.[34][38] As BIFs erlang:is_bitstring/1 e erlang:is_binary/1 podem ser utilizadas para verificar se um dado termo é um bit string ou binary, respectivamente.[34]

Reference

References (referências) são valores únicos que atuam como identificadores globais de uso geral. O runtime garante que cada referência gerada será globalmente única entre aquelas já presentes na instância atual do runtime e também nos demais nodes (nós) conectados a ela na rede.[34][37] Referências são geradas através da BIF erlang:make_ref/0.[34]

% Exemplo de execução na Eshell.

1> ReferenciaExemplo = erlang:make_ref().
#Ref<0.246453956.907280389.168899>

Fun

Funs (funções) representam objetos funcionais — objetos que representam funções no programa.[34] Funs possibilitam a criação funções anônimas e a passagem de funções em si — não seus nomes — como argumento para outras funções, sendo o pelo qual Erlang suporta funções de alta ordem.[34] As BIFs erlang:is_function/1 e erlang:is_function/2 podem ser utilizadas para verificar se uma determinada variável é uma função e se é uma função de determinada aridade, respectivamente.[34]

% Exemplo de execução na Eshell.

% FuncaoExemplo é uma função que não recebe argumentos e retorna o atom 'ok'.
1> FuncaoExemplo = fun() -> ok end.
#Fun<erl_eval.43.105768164>

% FuncaoExemplo é uma função.
2> erlang:is_function(FuncaoExemplo).
true

% FuncaoExemplo é uma função de aridade 0 (não recebe argumentos).
3> erlang:is_function(FuncaoExemplo, 0).
true

% FuncaoExemplo não é uma função de aridade 1 (recebe 1 argumento).
4> erlang:is_function(FuncaoExemplo, 1).
false

Port Identifier

Port Identifiers (identificadores de porta) representam uma referência para uma porta aberta no runtime.[34]

Process Identifier (PID)

Process Identifiers ou PIDs (identificadores de processo) representam uma referência para um processo vivo no runtime; cada processo possui um PID que o identifica.[34] Assim como referências, PIDs são globais e únicos entre os processos vivos na instância atual do runtime e também nos demais nodes (nós) conectados a ela na rede; entretanto um PID de um processo terminado pode ser reutilizado futuramente para identificar um novo processo.[34]

% Exemplo de execução na Eshell.

1> PIDProcessoAtual = erlang:self().
<0.121.0>

2> PIDOutroProcesso = erlang:spawn(fun() -> ok end).
<0.133.0>

Tipos compostos

Tuple

Tuples ou tuplas são um tipo de dados composto que mantém consigo uma quantidade fixa de termos.[34] Cada termo presente em uma tupla recebe a designação de elemento dessa tupla.[34]

{Termo1, Termo2, ..., TermoN}
% Exemplo de execução na Eshell.

1> Pessoa = {joão, 24, {julho, 29}}.
{joão, 24, {julho, 29}}

2> erlang:is_tuple(Pessoa).
true

% Pessoa é uma tupla composta por 3 termos.
3> erlang:tuple_size(Pessoa).
3

4> erlang:element(1, Pessoa).
joão

5> erlang:element(3, Pessoa).
{julho,29}

6> Pessoa2 = erlang:setelement(3, Pessoa, {agosto, 8}).
{joão, 24, {agosto, 8}}

Map

Maps ou mapas são um tipo de dados composto que atuam como arrays associativos (dicionários).[34] Um mapa mantém consigo uma quantidade variável de associações chave-valor — cada termo chave é mapeado para um termo valor.[34] Cada associação chave-valor de um mapa é chamada de par associativo. Um par associativo constitui um elemento de um mapa.[34]

#{Chave1 => Valor1, ..., ChaveN => ValorN}
% Exemplo de execução na Eshell.

1> Pessoa = #{nome => joão, idade => 24, aniversário => {julho, 29}}.
#{nome => joão, idade => 24, aniversário => {julho, 29}}

2> erlang:is_map(Pessoa).
true

% Pessoa é um mapa composto por 3 elementos (pares associativos).
3> maps:size(Pessoa).
3

4> maps:get(aniversário, Pessoa).
{julho, 29}

5> Pessoa2 = maps:update(idade, 25, Pessoa).
#{nome => joão, idade => 25, aniversário => {julho, 29}}

% Sintaxe alternativa para atualizar um mapa.
6> Pessoa3 = Pessoa2#{aniversário => {agosto, 8}}.
#{nome => joão, idade => 25, aniversário => {agosto, 8}}

% Novos pares associativos podem ser inseridos em um mapa.
7> Pessoa4 = Pessoa3#{pais => brasil, hobby => wikipedista}.
#{nome => joão, idade => 25, aniversário => {agosto, 8}, pais => brasil, hobby => wikipedista}

List

Lists ou listas são um tipo de dados composto que mantêm consigo uma quantidade variável de termos.[34] Cada termo presente em uma lista é designado como um elemento dessa lista.[34]

[Termo1, Termo2, ..., TermoN]

Formalmente, uma lista é ou uma lista vazia [] ou consiste em uma cabeça (primeiro elemento, também chamada de head) e uma cauda (elementos restantes da lista, também chamada de tail), onde a cauda também é uma lista. Uma lista pode então ser expressa como [H|T]. Dessa forma, a expressão [Termo1, ..., TermoN] pode ser expressa também como [Termo1|[...|[TermoN|[]]]].[34]

% Exemplo de execução na Eshell.

1> Dados = [joão, 24, {julho, 29}].
[joão, 24, {julho, 29}]

2> [H|T] = Dados.
[joão, 24, {julho, 29}]

3> H.
joão

4> T.
[24, {julho, 29}]

5> Lista2 = [wikipedista|[H] ++ T].
[wikipedista, joão, 24, {julho, 29}]

6> erlang:length(Lista2).
4

Açúcares sintáticos

String

Em Erlang, strings não constituem um tipo primitivo; em vez disso, strings são expressas como sequências de caracteres enclausurados por aspas (") que são tratadas pelo runtime como uma lista de inteiros que representam os valores ASCII ou code-points Unicode dos respectivos caracteres; a string "Wikipédia", portanto, pode ser expressa como a lista [$W, $i, $k, $i, $p, $é, $d, $i, $a] e também como lista [87, 105, 107, 105, 112, 233, 100, 105, 97].[34]

Boolean

Erlang não possui um tipo booleano primitivo; em vez disso, o runtime utiliza os atoms true e false para representar um valor lógico verdadeiro ou falso, respectivamente.[34]

Record

Records ou registros constituem estruturas de dados com campos nomeados, semelhante aos structs em C; no entanto, um registro não define um tipo; em vez disso, registros são traduzidos para tuplas durante a compilação e interpretados como tais pelo runtime, exceto que passa a ser permitido referir-se a um elemento em uma tupla pelo nome de seu campo e não apenas pela sua posição.[34] Dado um registro -record(pessoa, {nome, idade}), uma possível instância #pessoa{nome = joão, idade = 24} equivale a tupla {pessoa, joão, 24}.

% Exemplo de execução na Eshell.

1> -record(pessoa, {nome, idade}).
ok

2> criar_pessoa(Nome, Idade) -> #pessoa{nome = Nome, idade = Idade}.
ok

3> P1 = criar_pessoa(joão, 24).
#pessoa{nome = joão, idade = 24}

4> P1#pessoa.nome.
joão

5> P1#pessoa.idade.
24

6> P2 = P1#pessoa{idade = 25}.
#pessoa{nome = joão, idade = 25}

Linguagem Funcional

Resumir
Perspectiva

Veja o código abaixo:

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

 fac(0) -> 1;
 fac(N) -> N * fac(N-1).

Abaixo está a implementação de um algoritmo Quicksort

 %% quicksort:qsort(List)
 %% Classificar uma lista de itens
 -module(quicksort).
 -export([qsort/1]).

 qsort([]) -> [];
 qsort([Pivot|Rest]) ->
     qsort([X || X <- Rest, X < Pivot]) ++ [Pivot] ++ qsort([Y || Y <- Rest, Y >= Pivot]).

O exemplo acima invoca a função recursiva qsort até que não reste nada a ser classificado. A expressão [X || X <- Rest, X < Pivot] é uma lista de abrangência, o que significa “construir uma lista de elementos X tal que X pertence ao Resto e X é menor que Pivot“.

Uma função de comparação pode ser usada, no entanto, a ordem em que se baseia o retorno do código Erlang (verdadeiro ou falso) precisa ser alterada. Se, por exemplo, queremos uma lista ordenada onde a < 1 seja verdadeiro (true).

O seguinte código pode classificar listas de acordo com o tamanho:

 -module(listsort).
 -export([by_length/1]).

  by_length(Lists) ->
     qsort(Lists, fun(A,B) when is_list(A), is_list(B) -> length(A) < length(B) end).

  qsort([], _)-> [];
  qsort([Pivot|Rest], Smaller) ->
      qsort([X || X <- Rest, Smaller(X,Pivot)], Smaller)
      ++ [Pivot] ++
      qsort([Y ||Y <- Rest, not(Smaller(Y, Pivot))], Smaller).

Distribuição e Linguagem Orientada a Concorrência

Resumir
Perspectiva

A principal vantagem de Erlang é suporte a concorrência. Tem um pequeno, mas poderoso, conjunto de funções primitivas para criar processos e fazer com que eles se comuniquem. Processos são o principal meio de criar uma aplicação em Erlang.

Processos, como o sistema operacional (ao contrário de “Green threads” e threads do sistema operacional), não são compartilhadas entre si.

A sobrecarga mínima estimada para cada um é de 300 palavras (4 bytes por palavra sobre plataformas 32-bit, 8 bytes por palavra sobre plataformas 64-bit), e por isso muitas delas podem ser criadas sem degradar o desempenho (foi possível executar 20 milhões de processos[39]).Erlang suporta processamento simétrico na versão R11B desde maio de 2006.

O processo de comunicação é feito através de uma mensagem não-compartilhada em um sistema assíncrono: cada processo tem uma “caixa postal”, uma fila de mensagens enviadas por outros processos, que ainda não foram consumidos.

Um processo utiliza um meio primitivo para recuperar mensagens que correspondam aos padrões desejados.

Uma mensagem de rotina examina mensagens em cada turno, até que um deles seja correspondente. Quando a mensagem é consumida (retirada da caixa postal), o processo recomeça sua execução.Uma mensagem pode incluir qualquer estrutura de Erlang, incluindo funções primitivas (inteiros, ponto flutuante, caracteres,átomos), tuplas, listas e funções.

Exemplo de código:

 % cria um processo e chama a função web:start_server(Port, MaxConnections)
 ServerProcess = spawn (web, start_server, [Port, MaxConnections]),

 % cria um processo remoto e chama a função web:start_server(Port, MaxConnections) na máquina RemoteNode
 RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),

 % envia para {pause, 10} a mensagem (uma tupla com o átomo "pause" e um número "10") para ServerProcess (assincronamente)
 ServerProcess ! {pause, 10},

 % recebe as mensagens enviadas para este processo
 receive
         a_message -> do_something;
         {data, DataContent} -> handle(DataContent);
         {hello, Text} -> io:format("Ola tenho mensagem: ~s", [Text]);
         {goodbye, Text} -> io:format("Tenho mensagem de saídae: ~s", [Text])
 end.

Tal como mostra o exemplo, existe um suporte de processos distribuídos. Processos podem ser criados em nós (nodes, em inglês) remotos, e a comunicação com eles é transparente (ou seja, a comunicação com os processos remotos é feita exatamente como a comunicação entre os processos locais).

A Concorrência suporta o método primário para tratamento de erro em Erlang. Quando um processo falha, ele é fechado e envia uma mensagem para o processo controlador tomar alguma decisão.

Esta forma de controle de erros pode aumentar a durabilidade e reduzir a complexidade do código.

Exemplos de código

Resumir
Perspectiva

Concorrência

Cálculo dos fatoriais de uma sequência de números inteiros

% Arquivo example.erl

-module(example).
-export([fact_from_1_to_N/1]).

fact(0) -> 1;
fact(N) -> N * fact(N-1).

fact_from_1_to_N(N) ->
    % PID do processo principal.
    Main = erlang:self(),
    
    % Para cada inteiro X de 1 a N, inclusos, cria um processo filho ("worker")
    % cuja tarefa é enviar para o processo principal uma mensagem composta
    % por uma tupla contendo seu PID, o inteiro X que lhe fora designado
    % e o fatorial de X calculado.
    Workers = [ erlang:spawn_link(fun() -> Main ! {erlang:self(), {X, fact(X)}} end) || X <- lists:seq(1, N) ],
    
    % Recebe a mensagem enviada por cada processo filho executado,
    % retornando uma lista composta pelas tuplas dos inteiros X designados 
    % e seus respectivos fatoriais calculados.
    Result = [ receive {Pid, {_X, _Fact} = Output} -> Output end || Pid <- Workers ],
    Result.
% Exemplo de execução na Eshell.

1> c("example.erl").
{ok, example}

2> example:fact_from_1_to_N(25).
[
    {1,1},
    {2,2},
    {3,6},
    {4,24},
    {5,120},
    {6,720},
    {7,5040},
    {8,40320},
    {9,362880},
    {10,3628800},
    {11,39916800},
    {12,479001600},
    {13,6227020800},
    {14,87178291200},
    {15,1307674368000},
    {16,20922789888000},
    {17,355687428096000},
    {18,6402373705728000},
    {19,121645100408832000},
    {20,2432902008176640000},
    {21,51090942171709440000},
    {22,1124000727777607680000},
    {23,25852016738884976640000},
    {24,620448401733239439360000},
    {25,15511210043330985984000000}
]

Computação distribuída

Soma dos números inteiros de uma lista

% Arquivo exemple.erl

-module(example).
-export([distributed_sum/2]).

-doc """
Tenta dividir uma lista em partições (chunks) de comprimentos iguais a N.
Caso N não seja um múltiplo do comprimento da lista, a última partição
resultante terá menos elementos que as demais.
""".
split_list_in_chunks(List, N) -> split_list_in_chunks(List, N, N, [], []).
split_list_in_chunks(List, N, Left, Current, Accumulator) ->
    case List of
        [] ->
            case Current of
                [] -> lists:reverse(Accumulator);
                Remaining -> lists:reverse([lists:reverse(Remaining) | Accumulator])
            end;
        [First | Rest] ->
            Chunk = [First | Current],
            case Left > 1 of
                true -> split_list_in_chunks(Rest, N, Left - 1, Chunk, Accumulator);
                false -> split_list_in_chunks(Rest, N, N, [], [lists:reverse(Chunk) | Accumulator])
            end
    end.

-doc """
Realiza a soma de uma lista de números inteiros de forma distribuída entre
os nós solicitados.
""".
distributed_sum([], _) -> 0;
distributed_sum([0], _) -> 0;
distributed_sum(NumbersList, NodesList) ->
    % Divide a lista de números a serem somados em partições (chunks) a serem
    % distribuídas entre os nós. A fim de realizar uma divisão exata (sem casas decimais),
    % utiliza-se o operador matemático "div" e não o operador "/".
    ChunkSize = erlang:length(NumbersList) div erlang:length(NodesList),
    ChunksList = split_list_in_chunks(NumbersList, ChunkSize),
    
    % Associa cada partição da lista com um nó. Caso haja mais nós que partições, preenche
    % a lista de partições com a partição [0]. Caso haja mais partições que nós, um nó
    % selecionado aleatóriamente é delegado para lidar com as partições restantes.
    RandomNode = lists:nth(rand:uniform(erlang:length(NodesList)), NodesList),
    ChunksAndNodes = lists:zip(ChunksList, NodesList, {pad, {[0], RandomNode}}),
    
    % Envia uma requisição para cada nó de forma assíncrona, requisitando a execução da função
    % lists:sum(Chunk) para somar os valores de suas respectivas partições.
    RequestsList = [ erpc:send_request(Node, lists, sum, [Chunk]) || {Chunk, Node} <- ChunksAndNodes ],
    
    % Aguarda a finalização de cada request por até 1000 milisegundos.
    ResponsesList = [ erpc:wait_response(RequestId, 1000) || RequestId <- RequestsList ],
    
    % Obtém as somas de cada partição calculadas por cada nó.
    ResultList = [ Result || {response, Result} <- ResponsesList],
    
    % Soma as somas de cada partição em um único número inteiro.
    lists:sum(ResultList).

É importante destacar que, para fins de simplicidade, a implementação exemplificada acima não é suficientemente robusta e desconsidera cenários como falhas de comunicação com os demais nós, o que pode resultar em erros ou resultados incorretos.

# Ambiente shell

$ erl -name "a@127.0.0.1" -setcookie "examplecookie"

Suponha que um comando similar ao acima seja executado outras três vezes numa máquina local ou remota — contudo alterando o parâmetro -name para cada um dos valores b@127.0.0.1, c@127.0.0.1 e d@127.0.0.1, de modo com que haja ao todo quatro instâncias do runtime sendo executadas simultaneamente. A partir da instância a@127.0.0.1, é possível realizar uma soma dos números inteiros de uma lista de forma concorrente e distribuída entre a instância atual e os demais nós.

% Exemplo de execução na Eshell (instância a@127.0.0.1).

1> Sequencia = lists:seq(0, 100_000).
[0, 1, 2, 3, 4, 5, 6|...]

2> Nos = ['a@127.0.0.1', 'b@127.0.0.1', 'c@127.0.0.1', 'd@127.0.0.1'].
['a@127.0.0.1', 'b@127.0.0.1', 'c@127.0.0.1', 'd@127.0.0.1']

3> c("example.erl").
{ok, example}

4> example:distributed_sum(Sequencia, Nos).
5000050000

Ver também

Referências

  1. «Erlang/OTP 27.3 Release - Erlang/OTP». Erlang.org (em inglês). Consultado em 8 de março de 2025
  2. «Influences - The Rust Reference». doc.rust-lang.org. Consultado em 8 de março de 2025
  3. Armstrong, Joe (setembro de 2010). «Erlang». Communications of the ACM (em inglês) (9): 68–75. ISSN 0001-0782. doi:10.1145/1810891.1810910. Consultado em 6 de março de 2025
  4. Ghaffari, Amir (2015). «2.4 The Erlang Programming Language». The scalability of reliable computation in Erlang. PhD thesis. Glasglow: University of Glasgow. pp. 24–42
  5. «Compilation and Code Loading — Erlang System Documentation v27.3». www.erlang.org. Consultado em 6 de março de 2025
  6. Lutz, Michael J. (outubro de 2013). «The Erlang approach to concurrent system development». IEEE: 12–13. doi:10.1109/fie.2013.6684776. Consultado em 6 de março de 2025
  7. David R. Naugler (abril de 2008). «Learning the Erlang programming language» (em inglês). ACM Digital Library. Consultado em 24 de junho de 2011
  8. «Processes — Erlang System Documentation v27.3». www.erlang.org. Consultado em 6 de março de 2025
  9. «supervisor — stdlib v6.2.1». www.erlang.org. Consultado em 6 de março de 2025
  10. «os_mon». www.erlang.org. 2025. Consultado em 6 de março de 2025
  11. erlang/otp, Erlang/OTP, 6 de março de 2025, consultado em 7 de março de 2025
  12. Armstrong, Joe (9 de junho de 2007). «A history of Erlang». ACM (em inglês). ISBN 978-1-59593-766-7. doi:10.1145/1238844.1238850. Consultado em 7 de março de 2025
  13. atomvm/AtomVM, atomvm, 6 de março de 2025, consultado em 7 de março de 2025
  14. Branch, Philip; Weinstock, Phillip (29 de agosto de 2024). «Functional Programming for the Internet of Things: A Comparative Study of Implementation of a LoRa-MQTT Gateway Written in Elixir and C++». Electronics (em inglês) (17). 3427 páginas. ISSN 2079-9292. doi:10.3390/electronics13173427. Consultado em 7 de março de 2025
  15. GetFirefly/firefly, Firefly, 4 de março de 2025, consultado em 7 de março de 2025
  16. Cesarini, Francesco (11 de setembro de 2019). «Companies Who Use Erlang». Erlang Solutions (em inglês). Consultado em 6 de março de 2025
  17. «ejabberd & Nintendo Switch NPNS». ProcessOne (em inglês). 4 de junho de 2019. Consultado em 6 de março de 2025
  18. «Ellemtel and the axe». ericsson.com (em inglês). Consultado em 10 de março de 2025
  19. Wikstrom, C. (1996). «Implementing distributed real-time control systems in a functional programming language». IEEE Comput. Soc. Press: 20–26. ISBN 978-0-8186-7515-7. doi:10.1109/WPDRTS.1996.557430. Consultado em 10 de março de 2025
  20. Lindhult, Johan; Lindell, Bo (2002). The Execution Model of APZ/PLEX - An Informal Description. Västerås: Mälardalen University. pp. 1–43
  21. Fridlund, M. (2000). Edquist, Charles; Hommen, Leif; Tsipouri, Lena, eds. «Switching Relations and Trajectories: The Development Procurement of the Axe Swedish Switching Technology». Boston, MA: Springer US: 143–165. ISBN 978-1-4613-7084-0. doi:10.1007/978-1-4615-4611-5_7. Consultado em 10 de março de 2025
  22. Armstrong, Joe (agosto de 1997). «The development of Erlang». ACM (em inglês): 196–203. ISBN 978-0-89791-918-0. doi:10.1145/258948.258967. Consultado em 10 de março de 2025
  23. Däcker, Bjarne (2000). Concurrent Functional Programming for Telecommunications: A Case Study of Technology Introduction (PDF). Estocolmo: Royal Institute of Technology. pp. 11–44
  24. Armstrong, J.L.; Virding, S.R. (1990). «ERLANG - an experimental telephony programming language». IEEE: 43–48. doi:10.1109/ISS.1990.765711. Consultado em 9 de março de 2025
  25. Joe Armstrong, "History of Erlang", em HOPL III: Proceedings of the third ACM SIGPLAN conference on History of programming languages, 2007, ISBN 978-1-59593-766-X
  26. Armstrong, J. L.; Virding, S. R.; Williams, M. C. (27 de janeiro de 1992). «Use of Prolog for developing a new programming language». Institute of Electrical Engineers. The Practical Application of Prolog
  27. «Erlang, the mathematician?». Erlang-questions -- General Erlang/OTP discussions. Consultado em 13 de janeiro de 2012
  28. Aït-Kaci, Hassan (12 de agosto de 1991). Warren's Abstract Machine: A Tutorial Reconstruction (em inglês). [S.l.]: The MIT Press
  29. «A Brief History of the BEAM Compiler - Erlang/OTP». web.archive.org. 6 de março de 2025. Consultado em 10 de março de 2025
  30. Armstrong, J.; Dacker, B. O.; Virding, S. R.; Williams, Mike (1992). «Implementing a functional language for highly parallel real time applications». Consultado em 11 de março de 2025
  31. Armstrong, Joe; Virding, Robert; Williams, Mike (1993). Concurrent programming in Erlang 2nd print ed. New York, NY: Prentice-Hall
  32. «Concurrency Oriented Programming in Erlang» (PDF). 2 de novembro de 2002. Consultado em 13 de janeiro de 2012
  33. «question about Erlang's future». Erlang-questions -- General Erlang/OTP discussions. 6 de julho de 2010. Consultado em 13 de janeiro de 2012
  34. «Data Types — Erlang System Documentation v27.3». www.erlang.org. Consultado em 7 de março de 2025
  35. «Erlang -- Advanced». www.erlang.org. Consultado em 9 de março de 2025
  36. «System Limits — Erlang System Documentation v27.3». www.erlang.org. Consultado em 7 de março de 2025
  37. «Expressions — Erlang System Documentation v27.3». www.erlang.org. Consultado em 8 de março de 2025
  38. Ulf Wiger (14 de novembro de 2005). «Stress-testing erlang». comp.lang.functional.misc. Consultado em 25 de agosto de 2006

Ligações externas

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.