Erlang: bad parts
- 4 minutes read - 848 wordsAlguns comentários sobre as questões contidas nesta apresentação On Network Configuration, Distributed and Concurrent Programming with Erlang Chalmers sobre o tema “Erlang bad parts”:
The syntax
Pessoalmente, eu acho a sintaxe da linguagem (tanto Erlang quanto Elixir) bem prática. Mas ao mesmo tempo, se você comparar com qualquer outra linguagem (exemplo: C ou Javascript) vai notar algumas construções esquisitas.
Mas, depois de um tempo tudo fica confortável e prático. A dica aqui é não focar na sintaxe, busque entender o paradigma funcional e como a sintaxe ajuda nisso.
No real strings
Verdade.
Em Erlang não existe strings:
<<"Isto é uma string?">>
O que existe é o tipo de dado binary no qual pode ser qualquer tipo de dado, inclusive representações de string.
Geralmente temos lista de inteiros representando strings, exemplo:
"Isto é uma lista de inteiros"
Já em Elixir, é a mesma coisa. O compilador faz o trabalho de transformar uma string em binary
"Isto é um binary"
Após inspecionar o código compilado, veremos um binary.
binary possibilita fazer pattern match e também oferece uma boa performance se comparado com listas.
The broken if..
Raramente utilizado, mas pode ser usado. Acho que usei 4 vezes em 10 anos. Entretanto lendo o código Erlang/OTP você vai ver vários casos de como utilizar.
No scoping rules or actually just one - the whole clause!
Sim, é verdade. Por isso é importante não criar funções gigantes.
Being a dynamically typed language, static type checking is a very difficult problem
Este é um ponto bem sério. Muitas vezes o desenvolvedor apenas quer escrever código com um pouco de documentação e quando estamos em uma linguagem dinamicamente tipada precisamos nos preocupar em como descrever o tipo de dado de cada função. Exemplo Erlang User defined types:
-type suite() :: spades | clubs | hearts | diamonds.
-type value() :: 1..10 | j | q | k. -type card() :: {suite(), value()}.
-spec kind(card()) -> face | number.
kind({_, A}) when A >= 1, A =< 10 -> number; kind(_) -> face.
-spec é utilizado para marcar funções ajudando na análise estática (utilizando a ferramenta dialyzer) e também na documentação do código.
dialyzer pode ajudar bastante detectando erros como por exemplo: chamando uma função com tipos de parâmetros errados. Introduzir dialyzer já no começo do projeto reduz a quantidade de bugs no código.
Algumas vezes o dialyzer não vai ajudar. Como por exemplo utilizar chamadas dinâmicas usando MFA (Module, Function, Arity), e aí você só vai descobrir erros durante a execução da aplicação.
Acho que a dica para evitar situações é escrever anotações specs, investir em documentação e testes.
libraries are inconsistent - evolution vs design..
Esta palestra Security versus interoperability ajuda a responder essa questão. Pois muitas vezes a evolução está presa com determinados aspectos que não podem ser mudados facilmente.
Há uma outra palestra no qual podemos entender um pouco mais a questão “The Mess We’re In” by Joe Armstrong.
Acho que o ponto central é como manter a BEAM VM evoluindo e ao mesmo tempo ter estabilidade e segurança no design das aplicações.
No obvious support for abstraction, with some support bolted on afterwards, e.g., records
records
em Erlang e defstruct
em Elixir servem para definir abstrações.
Entretanto, dentro do paradigma funcional, precisamos definir as nossas funções
dentro de módulos.
Na maior parte do tempo usamos mapas, listas, proper lists, records e structs para definir as abstrações de dados. Vale os princípios gerais de SOLID.
Alias o livro Programming Erlang (2nd edition), no capítulo 4 Modules and Functions; o autor implementa um pequeno código demonstrando esta questão de abstrações entre linguagem C, Java e Erlang.
Em Erlang, não temos o conceito de namespace, então o nome dos módulos pode colidir com algum outro módulo existente. Por isso, geralmente definimos o nomes dos módulos prefixando algum nome que faz sentido. Exemplo: poli_server.erl, poli_metric.erl.
Já em Elixir, os módulos são nomeados utilizando defmodule
. Podendo ter vários
defmodule dentro de um mesmo arquivo. Durante a compilação os módulos são
extraídos e compilados em arquivos separados. Isso evita colisão com algum
projeto já existente.
Elixir provê suporte para polimorfismo, utilizando Protocols.
“too easy” to build complex applications fast; technical debt might build fast by using libraries causing too tight coupling; causes large problems later; normal software engineering principles still apply;
Sim, é verdade. Depois de um ciclo de desenvolvimento você vai ter a primeira versão da aplicação pronta para produção. Mas não significa que o trabalho acabou.
Creio que a questão seja mais relacionada com o seguinte modelo mental, usado quando criamos as aplicações:
- paradigma usando funcional
- sharing nothing
- trocas de mensagens entre processos para comunicação
- fail fast, alguma outra parte do sistema vai cuidar da falha
- menos programação defensiva, mais happy path
Após a primeira versão, pode ser o momento de isolar partes da aplicação, extrair bibliotecas, fazer um profile dos principais blocos e otimizar os que forem necessários.
Enfim, como recomendação de leitura, segue alguns links: