AVISO: esta série é uma fantasia satírica baseada em fatos reais sobre Elixir. Nada aqui deve ser levado ao pé da letra. Ao final de cada capítulo, os fatos por trás da lenda são revelados. Tags: #satire #humor #elixir #enchiridium
Elixir Enchiridium — Tomo IV: A Sintaxe
Prefácio do Tomo
No Tomo I, contamos a história de como Elixir nasceu. No Tomo II, abrimos a máquina. No Tomo III, abrimos o OTP. Agora, no Tomo IV, vamos abrir a sintaxe.
A sintaxe do Elixir é frequentemente descrita como “bonita”. Mas o que isso significa? Uma linguagem não é bonita por acaso. Ela é bonita porque cada operador, cada palavra-chave, cada construção foi projetada com um propósito. O = que não é atribuição. O |> que transforma código em poesia. O when que guarda as funções. As macros que geram código. Os sigils que criam mágica.
Este tomo é sobre essa beleza. Não a beleza como ornamento, mas a beleza como engenharia. Cada capítulo parte de um fato verificável sobre a sintaxe do Elixir e o transforma em uma crônica fantástica. Porque a verdade sobre a sintaxe do Elixir já é fantástica o suficiente.
São cem capítulos. O quarto tomo de dez.
Comecemos pelo começo: o operador que não é o que parece.
Capítulo 1 — O = que Não é Igual a Nada
A lenda diz que, no Elixir, o = não é atribuição. É um pombo. Quando você escreve x = 1, um pombo voa até o lado direito, olha o valor, e decide se ele bate com o padrão do lado esquerdo. Se bater, o pombo pousa. Se não bater, o pombo faz cocô no seu terminal e o programa crasha com um MatchError.
A verdade é que, em Elixir, o operador = é chamado de operador de match. Ele é comparável ao sinal de igual em álgebra. Quando você escreve x = 1, o Elixir tenta unificar o lado esquerdo com o lado direito. Se a unificação for bem-sucedida, a variável x é vinculada ao valor 1. Se não for, uma exceção MatchError é levantada. O operador = não é uma atribuição no sentido tradicional; é uma declaração de que os dois lados devem ser iguais.
A lenda diz que, na primeira vez que um programador viu 1 = x funcionar, ele perguntou: “Por que isso não é erro?” O instrutor respondeu: “Porque = não é atribuição. É match.” O programador ficou em silêncio por dez minutos.
O fato por trás da lenda: Em Elixir, o operador = é o operador de match. Ele tenta unificar o lado esquerdo com o lado direito, comparável ao sinal de igual em álgebra. Se a unificação falhar, uma exceção MatchError é levantada. O operador é usado para pattern matching, não apenas para atribuição.
Gancho: Mas se = é um pombo, o que é o ^?
Capítulo 2 — O Pin Operator: O Feitiço que Impede o Pombo de Pousar
A lenda diz que o pin operator (^) é um feitiço. Quando você escreve ^x = 1, o pombo do match olha para o ^ e entende: “Não mexa nessa variável. Use o valor que ela já tem.” O pombo obedece. Ele não vincula. Ele apenas compara.
A verdade é que o pin operator é usado quando você quer fazer pattern matching contra o valor de uma variável existente, em vez de vinculá-la. Por exemplo, se x = 1 e você escreve ^x = 1, o match verifica se o lado direito é igual ao valor atual de x. Se você escrevesse x = 1 sem o pin, x seria vinculado a 1 novamente. O pin operator é essencial para distinguir entre match e rebind.
A lenda diz que, na primeira vez que um programador usou ^, ele perguntou: “Por que o ^?” O instrutor respondeu: “Porque o pombo precisa saber quando não deve pousar.”
O fato por trás da lenda: O pin operator (^) é usado para fazer pattern matching contra o valor de uma variável existente, em vez de vinculá-la. Ele é essencial para distinguir entre match e rebind.
Gancho: O pombo pousa ou não pousa. Mas há um operador que transforma código em poesia: o pipe.
Capítulo 3 — O Pipe: A Tubulação que Transforma Código em Poesia
A lenda diz que o pipe operator (|>) foi inspirado em um encanamento. O código flui como água: entra por um lado, sai pelo outro, e no meio faz transformações. Se o encanamento estiver certo, a água chega limpa. Se estiver errado, vaza. O pipe foi inventado por um encanador que se cansou de escrever parênteses aninhados.
A verdade é que o pipe operator foi inspirado no operador |> do F# e em outras linguagens funcionais. Ele pega o resultado de uma expressão e o passa como primeiro argumento para a próxima função. Isso permite escrever código de forma encadeada e legível, como uma série de transformações. José Valim, criador do Elixir, disse uma vez que não se lembra exatamente de onde veio a ideia, mas que o F# já tinha pipes antes do Elixir.
A lenda diz que, na primeira vez que um programador usou |>, ele olhou para o código e disse: “Isso parece poesia.” O instrutor respondeu: “Parece. Mas é encanamento.”
O fato por trás da lenda: O pipe operator (|>) foi inspirado no operador |> do F#. Ele pega o resultado de uma expressão e o passa como primeiro argumento para a próxima função, permitindo escrever código de forma encadeada e legível.
Gancho: O pipe transforma. Mas há uma coisa que decora módulos e funções: os module attributes.
Capítulo 4 — Module Attributes: Os Selos que Contam Histórias
A lenda diz que os module attributes são selos. Cada módulo pode ser decorado com selos. @moduledoc conta a história do módulo. @doc conta a história da função. @spec conta a história dos tipos. @behaviour conta a história do contrato. Os selos são lidos pelo compilador, que decide se o código é digno.
A verdade é que module attributes em Elixir servem a três propósitos principais: (1) eles anotam módulos e funções com documentação (@moduledoc, @doc); (2) eles fornecem tipospecs (@spec, @type); e (3) eles permitem armazenar dados temporários em tempo de compilação. Atributos como @after_compile, @before_compile e @after_verify são hooks que o compilador invoca. Atributos personalizados podem ser usados como constantes em tempo de compilação.
A lenda diz que, na primeira vez que um programador usou @moduledoc, ele perguntou: “Isso é um comentário?” O instrutor respondeu: “Não. É um selo. O ExDoc vai ler.”
O fato por trás da lenda: Module attributes em Elixir servem para documentação (@moduledoc, @doc), tipospecs (@spec, @type), e armazenamento de dados em tempo de compilação. @after_compile, @before_compile e @after_verify são hooks invocados pelo compilador.
Gancho: Os selos contam histórias. Mas há uma coisa que gera histórias: as macros.
Capítulo 5 — Macros: Os Feitiços que Escrevem Feitiços
A lenda diz que as macros são feitiços que escrevem feitiços. Você escreve um feitiço, e o compilador o executa antes de compilar o resto do código. O feitiço recebe uma AST (Abstract Syntax Tree) como entrada e retorna uma AST como saída. Se o feitiço estiver certo, o código aparece. Se estiver errado, o compilador chora e se recusa a continuar.
A verdade é que macros são construções de tempo de compilação que recebem a AST do Elixir como entrada e retornam a AST do Elixir como saída. A AST em Elixir é representada como tuplas de três elementos e listas. A macro quote permite acessar a AST, e unquote permite injetar código nela. Macros são usadas para criar DSLs, estender a linguagem e reduzir boilerplate.
A lenda diz que, na primeira vez que um programador escreveu uma macro, ele perguntou: “Isso é magia?” O instrutor respondeu: “Não. É metaprogramação.” O programador perguntou: “Qual a diferença?” O instrutor respondeu: “Magia não compila.”
O fato por trás da lenda: Macros são construções de tempo de compilação que recebem a AST do Elixir como entrada e retornam a AST como saída. A AST é representada como tuplas de três elementos e listas. quote acessa a AST, unquote injeta código nela.
Gancho: As macros geram. Mas há uma coisa que guarda as funções: os guards.
Capítulo 6 — Guards: Os Guardiões que Só Deixam Passar o Que Merece
A lenda diz que os guards são guardiões. Eles ficam na porta das funções. Quando um argumento chega, o guardião olha para ele e decide: “Você pode entrar” ou “Você não pode entrar.” Se você não pode, o guardião chama o próximo guardião. Se nenhum guardião deixar, o FunctionClauseError aparece.
A verdade é que guards são uma forma de aumentar o pattern matching com verificações mais complexas. Eles começam com a palavra-chave when, seguida de uma expressão booleana. Guards suportam operadores de comparação (==, !=, ===, !==, >, >=, <, <=), operadores booleanos estritos (and, or, not), e funções de verificação de tipo (is_integer, is_binary, is_list, etc.). Você também pode definir guards personalizados com defguard e defguardp.
A lenda diz que, na primeira vez que um programador usou when, ele perguntou: “O que posso colocar aqui?” O instrutor respondeu: “Só o que o guardião entende.” O programador perguntou: “E se ele não entender?” O instrutor respondeu: “Aí ele chama a polícia.”
O fato por trás da lenda: Guards começam com when e são seguidos de uma expressão booleana. Eles suportam operadores de comparação, booleanos estritos, e funções de verificação de tipo. defguard e defguardp permitem definir guards personalizados.
Gancho: Os guardiões protegem. Mas há uma coisa que organiza dados: os structs.
Capítulo 7 — Structs e Protocols: As Fichas e os Tradutores
A lenda diz que os structs são fichas. Cada ficha tem um formato. Cada ficha guarda dados. E os protocols são tradutores. Eles olham para a ficha e decidem como traduzi-la. Se a ficha é um User, o protocolo a traduz de um jeito. Se é um Post, traduz de outro. Os protocolos nunca se confundem.
A verdade é que structs são mapas com um campo especial __struct__ que define o tipo. Protocols são mecanismos de polimorfismo que permitem despachar com base no tipo do primeiro argumento. O @derive permite derivar implementações de protocolos para structs. Protocolos como Inspect, Enumerable e String.Chars são implementados para vários tipos.
A lenda diz que, na primeira vez que um programador usou @derive, ele perguntou: “Isso é herança?” O instrutor respondeu: “Não. É polimorfismo.” O programador perguntou: “Qual a diferença?” O instrutor respondeu: “Herança é o que você faz. Polimorfismo é o que o protocolo faz.”
O fato por trás da lenda: Structs são mapas com um campo __struct__. Protocols permitem polimorfismo por despacho no tipo do primeiro argumento. @derive deriva implementações de protocolos para structs.
Gancho: Structs guardam. Protocols traduzem. Mas há uma coisa que cria strings: os sigils.
Capítulo 8 — Sigils: A Magia da Tilde
A lenda diz que os sigils são feitiços que começam com ~. O ~r cria uma expressão regular. O ~c cria uma charlist. O ~s cria uma string. O ~w cria uma lista de palavras. Cada sigil tem seu poder. Cada sigil tem seu delimitador. Os sigils nunca falham.
A verdade é que sigils começam com o caractere til (~), seguido de uma letra (ou letras maiúsculas) e um delimitador. O ~r cria expressões regulares, ~c cria charlists, ~s cria strings, ~w cria listas de palavras. Você também pode criar sigils personalizados definindo uma função sigil_*.
A lenda diz que, na primeira vez que um programador usou ~r, ele perguntou: “Isso é regex?” O instrutor respondeu: “Sim.” O programador perguntou: “E o ~?” O instrutor respondeu: “É magia.”
O fato por trás da lenda: Sigils começam com ~, seguido de uma letra e um delimitador. ~r cria regex, ~c cria charlists, ~s cria strings, ~w cria listas de palavras. Sigils personalizados são definidos com sigil_*.
Gancho: Os sigils criam. Mas há uma coisa que simplifica: o with.
Capítulo 9 — with: A Special Form que Simplifica o Fluxo
A lenda diz que o with é um feitiço. Ele pega várias expressões, e só continua se todas elas casarem. Se uma falhar, o else aparece. O with é usado para substituir case aninhados. Ele é limpo. Ele é sucinto. Ele é poderoso.
A verdade é que with é uma special form do Elixir (Kernel.SpecialForms.with/1). Ele permite encadear várias expressões com pattern matching. Cada expressão é avaliada em ordem; se uma falhar, o else (se presente) é executado. with é frequentemente usado para lidar com múltiplos resultados {:ok, _} ou {:error, _} sem aninhar case.
A lenda diz que, na primeira vez que um programador usou with, ele perguntou: “Isso substitui case?” O instrutor respondeu: “Não. Substitui case aninhado.” O programador perguntou: “E se eu precisar de else?” O instrutor respondeu: “Aí você usa else.”
O fato por trás da lenda: with é uma special form (Kernel.SpecialForms.with/1). Ele permite encadear expressões com pattern matching, executando o else se alguma falhar. É usado para simplificar fluxos com múltiplos case aninhados.
Gancho: O with simplifica. Mas há uma coisa que explica: o case.
Capítulo 10 — case: O Tribunal que Julga Cada Valor
A lenda diz que o case é um tribunal. Ele pega um valor e o julga. Cada cláusula é uma sentença. Se o valor casar com a cláusula, a sentença é executada. Se não casar com nenhuma, o CaseClauseError aparece. O tribunal é justo. O tribunal é imparcial.
A verdade é que case é uma expressão que permite comparar um valor com vários padrões. Cada cláusula tem um padrão e um corpo. O primeiro padrão que casar é executado. Se nenhum casar, uma exceção CaseClauseError é levantada. case é uma das formas mais comuns de controle de fluxo em Elixir.
A lenda diz que, na primeira vez que um programador usou case, ele perguntou: “Isso é um switch?” O instrutor respondeu: “Não. É um case.” O programador perguntou: “Qual a diferença?” O instrutor respondeu: “switch não tem pattern matching.”
O fato por trás da lenda: case permite comparar um valor com vários padrões. A primeira cláusula que casar é executada. Se nenhuma casar, uma exceção CaseClauseError é levantada.
Gancho: O case julga. Mas há uma coisa que captura erros: o try/rescue.
Epílogo Parcial do Tomo IV
Dez capítulos. Uma sintaxe. Mil processos. E um brasileiro que ouviu um sussurro.
Este foi o começo do Tomo IV — A Sintaxe. Ainda faltam 90 capítulos para completar este tomo. Nos próximos, vamos explorar: try/rescue, cond, compreensões, binários, strings, listas, mapas, tuplas, e muitos outros tópicos.
Até lá.
Agora, sério: Em Elixir, o operador = é o operador de match. O pin operator (^) faz match contra o valor de uma variável existente. O pipe operator (|>) foi inspirado no F#. Module attributes servem para documentação, tipospecs e dados em tempo de compilação. Macros recebem AST como entrada e retornam AST como saída. Guards usam when para verificações complexas. Structs são mapas com __struct__. Protocols permitem polimorfismo. Sigils começam com ~. with é uma special form. case compara um valor com padrões. As capivaras quânticas não são reais — mas deveriam ser. Este artigo é uma fantasia satírica baseada em fatos. Mantenha o aviso para não enganar ninguém.
Top comments (0)