<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linguagens-De-Programacao | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/linguagens-de-programacao/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/linguagens-de-programacao/index.xml" rel="self" type="application/rss+xml"/><item><title>Existem Linguagens Melhores pra programar com LLMs?</title><link>https://akitaonrails.github.io/2026/10/11/existem-linguagens-melhores-pra-programar-com-llms/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/10/11/existem-linguagens-melhores-pra-programar-com-llms/</guid><pubDate>Sun, 11 Oct 2026 21:00:00 -0300</pubDate><description>&lt;p&gt;Semana passada o DHH resolveu farmar engajamento com o formato mais preguiçoso que existe: converteu o Campfire, o app de chat dele, pra várias linguagens usando agentes de IA, e foi soltando os resultados de throughput um a um no X. Na série que ele postou, a versão em Rust saiu na frente das equivalentes em Elixir e Ruby. A thread inteira foi desenhada pra provocar, e provocou.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-partner="tweetdeck"&gt;&lt;p lang="en" dir="ltr"&gt;Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog&lt;/p&gt;</description><content:encoded><![CDATA[<p>Semana passada o DHH resolveu farmar engajamento com o formato mais preguiçoso que existe: converteu o Campfire, o app de chat dele, pra várias linguagens usando agentes de IA, e foi soltando os resultados de throughput um a um no X. Na série que ele postou, a versão em Rust saiu na frente das equivalentes em Elixir e Ruby. A thread inteira foi desenhada pra provocar, e provocou.</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">Last conversion of the Campfire from my token stash goes to JavaScript/Express. As with the others, I'm sure you can find more to optimize. But this gives you a sense of what order of magnitude these options exist within with agent-led development. https://t.co/UUapL99SJE https://t.co/2H2NXfeZog</p>&mdash; DHH (@dhh) <a href="https://twitter.com/dhh/status/2107077808182976817">October 5, 2026</a></blockquote>
<p>Não vou defender ele dessa vez. Uma conversão única, feita por agente, sem ajuste fino nenhum, não mede linguagem: mede o quanto o modelo conhece os idiomas de cada stack. É rage bait e ele sabe. Dito isso, eu faço o mesmo de vez em quando, e metade dos críticos dele também, então somos todos hipócritas e segue o jogo.</p>
<p>E o ponto fraco da metodologia dele virou tópico por conta própria: várias pessoas notaram que a versão em Rust tinha passado por bem mais iterações de agente que as outras, ou seja, mais rodadas de otimização. O @kurtome respondeu na prática: pegou a versão em Elixir e subiu as requisições da página principal em 10x, com otimizações óbvias que nenhuma primeira conversão faz, tipo cache e pooling assíncrono de conexões de banco.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">@dhh Here you go https://t.co/gUtK7Z7zG7<br><br>got a 10x speed up on the Room page requests, and sped up all the other requests significantly as well. the biggest gains came from some no brainers: implement caching and pool the db connections with proper async https://t.co/mbp94q9N0s</p>&mdash; kurt (.com) (@kurtome) <a href="https://twitter.com/kurtome/status/2106902762076467681">October 5, 2026</a></blockquote>
<p>A comparação dele, então, não era &ldquo;Rust contra Elixir&rdquo;: era &ldquo;Rust depois de N rodadas de ajuste contra Elixir depois de uma&rdquo;. E isso escancara a verdade que a thread finge ignorar: <strong>nenhum software de verdade sai com meia dúzia de prompts</strong>. Codar com IA brilha quando um engenheiro experiente pilota o agente rumo ao objetivo dele, e isso significa dezenas de iterações, não três.</p>
<p>A parte boa é que a isca foi útil pra mim. Eu venho fazendo <a href="/2026/09/15/novo-llm-benchmark-v4-retestando-todos-llms-parte-1/">benchmarks de LLM</a> e <a href="/2026/10/10/qual-modelo-de-decisao-classificador-de-ia-tipo-jev-e-melhor/">benchmark de modelos de decisão</a>, mas nunca tinha medido, de forma reproduzível, a pergunta que essa thread fingia responder: <strong>faz diferença qual linguagem você escolhe quando quem escreve o código é um LLM?</strong> Minha resposta curta já estava dada em dois posts meus:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Pressure under load is not a trivial problem to solve from scratch and I recommend you do not attempt to. It's a solved problem already and all solutions converge to what Elixir already brings out of the box. So yes. I use Rust all the time, but if I had to solve high scalability problems (particularly high volume message queues), I would think of Elixir first.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108587462595367353">October 9, 2026</a></blockquote>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Language wars are tiresome. And the reality is simpler if you're doing AI assisted coding.<br><br>- system level (drivers, fs, daemons, etc): Rust. C++ is you're extending existing legacy<br>- windows only desktop apps: C#<br>- iOS/MacOS/iPadOS: Swift<br>- multi-OS: Rust/Tauri, Qt/C++, Flutter, Electron (pick your poison)<br>- simple websites: Node/Typescript/Bun/Deno<br>- highly scalable backend: Elixir, parts in Rust, parts in Go<br>- CLI tools: Rust<br><br>Everything else, by default, Rust. Unless it's an edge case.</p>&mdash; Fabio Akita (@AkitaOnRails) <a href="https://twitter.com/AkitaOnRails/status/2108590592263352540">October 9, 2026</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>Não existe linguagem melhor, existe ferramenta certa<span class="hx:absolute hx:-mt-20" id="não-existe-linguagem-melhor-existe-ferramenta-certa"></span>
    <a href="#n%c3%a3o-existe-linguagem-melhor-existe-ferramenta-certa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aquela lista do post não é ranking, é mapa de terreno. Cada linguagem e cada framework foram desenhados pra um caso de uso, e nenhum cobre todos. Isso valia quando você escrevia cada linha na mão e continua valendo com agente. O LLM não muda a física do problema: ele só muda quem digita.</p>
<p>E o mapa vai além da linguagem. Sistema real tem infraestrutura em volta: Kubernetes quando a escala pede, SQLite por padrão porque a maioria dos apps nunca precisa de mais que isso, Postgres com pgBouncer quando precisa, Redis pra cache e chave-valor, RabbitMQ ou NATS pra fila. Escolher &ldquo;a stack&rdquo; no dia zero e achar que ela dura dez anos é fantasia. Não dura, e tudo bem.</p>
<p>Exemplo pessoal: web app novo, eu abro Ruby on Rails sem pensar duas vezes. É a solução mais completa de ponta a ponta que existe pra web, e com LLM escrevendo a maior parte do código, a produtividade do framework pesa ainda mais que a da linguagem. Conforme o sistema cresce, eu vou trocando as partes que pedem outra ferramenta: fila de alto volume sai do lado do Rails e vai pra algo da terra Erlang, começando por RabbitMQ, que já resolve a parte difícil de backpressure pra mim.</p>
<blockquote>
  <p>Nenhum software bom está pronto: ele cresce. O uso dita o que você substitui, reescreve e adiciona. A arquitetura final nunca é a do primeiro commit, e quem tenta acertar tudo no primeiro commit só gera documento morto.</p>

</blockquote>
<h2>A superstição dos tokens<span class="hx:absolute hx:-mt-20" id="a-superstição-dos-tokens"></span>
    <a href="#a-supersti%c3%a7%c3%a3o-dos-tokens" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A thread do DHH ressuscitou duas crenças que circulam desde que LLM virou ferramenta de trabalho:</p>
<ul>
<li><strong>&ldquo;Linguagem de script é melhor pra IA porque a sintaxe enxuta gasta menos tokens.&rdquo;</strong></li>
<li><strong>&ldquo;Linguagem fortemente tipada é melhor pra IA porque o compilador pega os erros, então precisa de menos teste e menos reparo.&rdquo;</strong></li>
</ul>
<p>As duas soam razoáveis e as duas são superstição até alguém medir. Eu apostava contra as duas, por experiência: compilador bom ajuda, mas não faz mágica, e concisão de sintaxe nunca foi o gargalo de um agente que lê e escreve arquivo o dia inteiro.</p>
<p>O que realmente separa projeto que anda de projeto que gira em círculo é disciplina de teste. Já escrevi que <a href="/2026/04/20/clean-code-para-agentes-de-ia/">TDD virou obrigação técnica, não filosofia</a>, e o benchmark deste texto é mais um dado na mesma direção. Não importa se você faz test-first ou test-after: importa que a suíte cresça junto com o código, que <a href="/2026/03/01/software-nunca-esta-pronto-4-projetos-a-vida-pos-deploy-e-por-que-one-shot-prompt-e-mito/">todo bug reportado por usuário vire teste de regressão</a> antes da correção, e que você nunca repita o mesmo erro duas vezes. Software sem usuário é software morto, então <a href="/2026/09/22/parem-de-inventar-desculpas-e-facam-mais-deploy-a-premissa-mudou/">release cedo, release sempre</a>, quebra rápido e conserta mais rápido ainda. IA fecha exatamente essa lacuna: o ciclo de reproduzir, testar e corrigir ficou barato. QA virou a habilidade mais valiosa do programador, muito acima de &ldquo;saber a sintaxe de Rust&rdquo;.</p>
<p>Disciplina vence compilador e vence linguagem. Essa era a minha hipótese. Foi medir.</p>
<h2>O benchmark<span class="hx:absolute hx:-mt-20" id="o-benchmark"></span>
    <a href="#o-benchmark" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Montei um experimento reproduzível, público no <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">repositório vibe-coding-benchmarks</a>, com os resultados por tentativa publicados em JSON e os rastros brutos fora do repo por contenção de credenciais. Não é um campeonato de linguagem, e o próprio design repete isso: é uma fotografia de uma tarefa real, medindo o que dá pra medir com honestidade.</p>
<h3>A tarefa<span class="hx:absolute hx:-mt-20" id="a-tarefa"></span>
    <a href="#a-tarefa" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma tarefa só, de propósito: manter um <strong>motor de livro-razão</strong> (ledger) multilocatário. O agente recebe um código v1 funcionando e precisa levá-lo a um contrato v2, com identidades por tenant, autorização, snapshots serializáveis, índice secundário único com troca de alias, journal com limite de capacidade, recuperação de grupos rasgados, checkpoint, migração de schema legado e IDs de retry duráveis. Não é FizzBuzz: é o tipo de sistema que exige invariante atravessando operações, exatamente onde LLM costuma tropeçar.</p>
<p>A correção é julgada por <strong>43 histórias de teste aplicadas depois da coleta</strong>, fora da vista do agente: as 27 públicas do T1 mais 16 ocultas. Passar é 43 de 43. As condições:</p>
<ul>
<li><strong>T0:</strong> contrato em prosa, exemplos de entrada e saída, um launcher funcionando. Sem suíte de testes.</li>
<li><strong>T1:</strong> o mesmo, mais 27 histórias públicas e um driver pra rodá-las.</li>
</ul>
<p>Cada tentativa tinha 15 minutos de orçamento, com no máximo duas continuações de 3 minutos. Na prática, nenhuma das 112 precisou de continuação.</p>
<h3>As linguagens e os modelos<span class="hx:absolute hx:-mt-20" id="as-linguagens-e-os-modelos"></span>
    <a href="#as-linguagens-e-os-modelos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Sete linguagens em três estratos, com os toolchains pinados e o Rust em modo seguro:</p>
<ul>
<li><strong>Nativas tipadas:</strong> Rust (safe, pinado), Haskell (GHC pinado).</li>
<li><strong>VM tipadas:</strong> C# (com nullable analysis), Kotlin, TypeScript (<code>strict</code>, <code>noUncheckedIndexedAccess</code>, <code>exactOptionalPropertyTypes</code>).</li>
<li><strong>Dinâmicas de script:</strong> Python e Ruby, sem checker nenhum.</li>
</ul>
<p>Quatro configurações de modelo, todas por assinatura, sem fallback pago: <strong>Kimi K3</strong> (high), <strong>GLM 5.3</strong> via OpenCode (high), <strong>Claude Opus 5.5</strong> (medium) e <strong>Codex com gpt-6 Sol</strong> (medium). Cruzando tudo: 7 linguagens × 4 modelos × 2 condições × 2 repetições = <strong>112 tentativas</strong>.</p>
<h3>O que esse design não mede<span class="hx:absolute hx:-mt-20" id="o-que-esse-design-não-mede"></span>
    <a href="#o-que-esse-design-n%c3%a3o-mede" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Antes dos números, os freios, porque eles fazem parte do resultado. É uma tarefa só, com duas repetições por célula: amostra pequena por construção. Cobertura de tokens é parcial em alguns modelos (dado faltante não é zero). O harness mudou no meio da campanha em três épocas de instrumentação. E atrasos de provedor contaminam tempo de parede: uma das tentativas do Opus passou metade do relógio esperando liberação de cota. Todos os números abaixo são descritivos, e eu aponto onde eles não sustentam conclusão.</p>
<p>Tem um freio adicional, de propósito: <strong>o nível de iteração</strong>. Meu desenho mede o que dá pra alcançar em todas as linguagens com níveis parecidos de iteração, em orçamento curto, sem um humano ajustando depois. É fotografia do piso, não do teto. Qualquer uma das 112 implementações provavelmente melhora com mais rodadas de agente, e as quatro stacks de fila idem: o que eu publico é o que cada linguagem entrega &ldquo;de fábrica&rdquo; sob o mesmo esforço, não o máximo que um engenheiro paciente extrairia dela.</p>
<blockquote>
  <p><strong>Disclaimer obrigatório antes de citar qualquer número deste texto por aí:</strong> todos os resultados a seguir valem pra <strong>este</strong> benchmark, com <strong>esta</strong> tarefa, <strong>estes</strong> modelos, <strong>estes</strong> prompts e <strong>este</strong> hardware. O seu caso pode variar violentamente. Se a sua tarefa não se parece com um motor de livro-razão, se o seu modelo não é um destes quatro, se a sua fila não é a minha fila, os números não se transferem. O código, as tarefas, as notas e o passo a passo pra reproduzir estão todos no <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">repositório no GitHub</a>. E se você discordar de algum resultado e essa discordância for importante pra sua empresa ou pro seu projeto, a única atitude honesta é a que eu tive: <strong>pare de argumentar e rode o seu próprio benchmark.</strong></p>

</blockquote>
<h2>Resultado 1: em acerto, os modelos de fronteira empataram no teto<span class="hx:absolute hx:-mt-20" id="resultado-1-em-acerto-os-modelos-de-fronteira-empataram-no-teto"></span>
    <a href="#resultado-1-em-acerto-os-modelos-de-fronteira-empataram-no-teto" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th>Configuração</th>
          <th style="text-align: right">Corretas no prazo</th>
          <th style="text-align: right">Corretas no limite</th>
          <th style="text-align: right">Falhas</th>
          <th style="text-align: right">Sem nota</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Kimi K3 high</td>
          <td style="text-align: right">17</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">2</td>
      </tr>
      <tr>
          <td>GLM 5.3 high (OpenCode)</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">5</td>
      </tr>
      <tr>
          <td>Opus 5.5 medium</td>
          <td style="text-align: right">25</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">3 (interrupção de infra)</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Codex / Sol medium</td>
          <td style="text-align: right">28</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">0</td>
      </tr>
  </tbody>
</table>
<p>Leitura: Sol passou nas 28 tentativas elegíveis e o Opus nas 25 dele (as 3 falhas foram infraestrutura, não código). <strong>53 de 53 tentativas elegíveis corretas entre os dois.</strong> A tarefa, feita pra ser difícil, foi fácil demais pra separar os modelos de fronteira, e muito menos as linguagens: com todo mundo passando em tudo, não existe ranking de acerto pra publicar.</p>
<p>A diferença apareceu nos modelos mais fracos, e na direção que a minha hipótese previa: <strong>teste fornecido ajudou</strong>. Kimi e GLM, com a suíte pública em mãos (T1), entregaram 24 artefatos corretos contra 16 sem ela (T0). O Kimi, em particular, passou nas 14 tentativas com testes fornecidos. É descritivo, não causal, mas é o sinal mais limpo do benchmark inteiro: o que mudou o resultado não foi a linguagem, foi a presença de teste.</p>
<p>Por linguagem, dentro de cada modelo, o acerto ficou espalhado entre 1/4 e 4/4 sem padrão que se repita entre modelos. As três falhas semânticas que eu auditei a fundo são instrutivas: um C# do GLM e um Python do Kimi perderam a mesma história por violar a mesma invariante entre operações, e um Haskell do Kimi errou um caminho de checkpoint. Nenhuma falha era erro de tipo.</p>
<blockquote>
  <p>Onde o modelo errou, errou invariante de domínio: o tipo de erro que compilador nenhum pega, em linguagem tipada ou dinâmica. O mito de que tipagem forte poupa o trabalho de entender o problema morre aí.</p>

</blockquote>
<h2>Resultado 2: linguagem concisa não economizou token<span class="hx:absolute hx:-mt-20" id="resultado-2-linguagem-concisa-não-economizou-token"></span>
    <a href="#resultado-2-linguagem-concisa-n%c3%a3o-economizou-token" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui vai a tabela que mata a primeira superstição. Medianas por linguagem no Sol, quatro tentativas válidas por célula:</p>
<table>
  <thead>
      <tr>
          <th>Linguagem</th>
          <th style="text-align: right">Tempo mediano</th>
          <th style="text-align: right">Tokens medianos</th>
          <th style="text-align: right">Linhas finais do código</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TypeScript</td>
          <td style="text-align: right">53 s</td>
          <td style="text-align: right">147,5 mil</td>
          <td style="text-align: right">~280</td>
      </tr>
      <tr>
          <td>Python</td>
          <td style="text-align: right">77 s</td>
          <td style="text-align: right">138,2 mil</td>
          <td style="text-align: right">~300</td>
      </tr>
      <tr>
          <td>C#</td>
          <td style="text-align: right">61 s</td>
          <td style="text-align: right">171,6 mil</td>
          <td style="text-align: right">~260</td>
      </tr>
      <tr>
          <td>Kotlin</td>
          <td style="text-align: right">65 s</td>
          <td style="text-align: right">170,6 mil</td>
          <td style="text-align: right">~210</td>
      </tr>
      <tr>
          <td>Haskell</td>
          <td style="text-align: right">75 s</td>
          <td style="text-align: right">184,7 mil</td>
          <td style="text-align: right">~265</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">82 s</td>
          <td style="text-align: right">185,6 mil</td>
          <td style="text-align: right">~380</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">80 s</td>
          <td style="text-align: right">188,1 mil</td>
          <td style="text-align: right">~650</td>
      </tr>
  </tbody>
</table>
<p>Ruby, o mais conciso em linhas (pouco mais da metade do Rust), custou praticamente os mesmos tokens do Rust no Sol, e no Opus foi <strong>o mais caro de todos</strong>, uns 205 mil tokens de mediana contra 94 mil do C#. Os mais baratos foram TypeScript e Python no Sol, C#, Haskell e Kotlin no Opus. Se &ldquo;menos linhas de código fonte = menos tokens de LLM&rdquo; fosse lei, Ruby tinha que liderar de longe. Ficou no fim.</p>
<p>E a segunda superstição? Auditei os rastros procurando os ciclos de reparo guiados por compilador que a tipagem forte supostamente evita. Resultado: <strong>zero ciclos de reparo de erro de compilação</strong> nas tentativas auditadas do Opus, em Ruby, Rust e Haskell. O Rust não precisou de correção nenhuma vinda do compilador; o Haskell teve uma limpeza de warning; o Ruby gastou seus tokens extras em ajuste voluntário de performance. O modelo de fronteira quase sempre escreve código que compila de primeira, e o esforço real vai pra verificação, não pra apaziguar o compilador.</p>
<p>Um contraponto que fecha o caixão da tese &ldquo;barato é bom&rdquo;: as células de <strong>menor</strong> consumo de token do GLM foram as falhas. TypeScript T0 por 50 mil tokens parece eficiente até você ver que são dois starters inalterados porque o modelo estourou o limite de saída. Token baixo pode significar desistência precoce.</p>
<h2>Resultado 3: sob carga, todo mundo empatou na fila<span class="hx:absolute hx:-mt-20" id="resultado-3-sob-carga-todo-mundo-empatou-na-fila"></span>
    <a href="#resultado-3-sob-carga-todo-mundo-empatou-na-fila" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aí vem a pergunta do DHH: Rust &ldquo;ganha&rdquo; de Elixir e Ruby quando o assunto é throughput. Montei a trilha separada pra isso: quatro workers de fila contra um RabbitMQ real, escritos com ajuda de agente, cada um com seu idioma natural:</p>
<ul>
<li><strong>Elixir</strong> com OTP e Broadway</li>
<li><strong>Rust</strong> com Tokio e Lapin</li>
<li><strong>Rails</strong> com Bunny</li>
<li><strong>Ruby puro</strong> com Bunny (controle)</li>
</ul>
<p>Um gerador publicava 10 mil mensagens de 4 KB no RabbitMQ, a 200 por segundo, e cada worker consumia o mais rápido que conseguia, batendo os efeitos num coletor HTTP/SQLite com atraso nominal de 50 ms, limitado a 2 CPUs e 1 GB de RAM. Dois perfis de concorrência:</p>
<table>
  <thead>
      <tr>
          <th>Stack</th>
          <th style="text-align: right">Handlers</th>
          <th style="text-align: right">Efeitos/s</th>
          <th style="text-align: right">Corretas/10.000</th>
          <th style="text-align: right">Pico de memória</th>
          <th>Drenou?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94,8</td>
          <td style="text-align: right">10.000</td>
          <td style="text-align: right">135 MiB</td>
          <td>sim</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">94,6</td>
          <td style="text-align: right">10.000</td>
          <td style="text-align: right">16 MiB</td>
          <td>sim</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92,7</td>
          <td style="text-align: right">10.000</td>
          <td style="text-align: right">79 MiB</td>
          <td>sim</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">92,5</td>
          <td style="text-align: right">10.000</td>
          <td style="text-align: right">38 MiB</td>
          <td>sim</td>
      </tr>
      <tr>
          <td>Elixir</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">84,8</td>
          <td style="text-align: right">7.915</td>
          <td style="text-align: right">161 MiB</td>
          <td>não</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88,2</td>
          <td style="text-align: right">8.151</td>
          <td style="text-align: right">23 MiB</td>
          <td>não</td>
      </tr>
      <tr>
          <td>Rails</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88,5</td>
          <td style="text-align: right">8.108</td>
          <td style="text-align: right">87 MiB</td>
          <td>não</td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td style="text-align: right">64</td>
          <td style="text-align: right">88,0</td>
          <td style="text-align: right">8.081</td>
          <td style="text-align: right">60 MiB</td>
          <td>não</td>
      </tr>
  </tbody>
</table>
<p>No perfil de 8 handlers, as quatro stacks entregaram a mesma vazão, 93 a 95 efeitos por segundo, e drenaram a fila inteira. O gargalo aparente era o coletor compartilhado (um lock em Python, um escritor SQLite), não as linguagens. No perfil de 64 handlers, todas deixaram backlog e estouraram o timeout do cliente em massa, então marquei essas células como não qualificadas: o protocolo forte, com coletor de group-commit e escada de 50 a 800 msg/s, está desenhado e não executado.</p>
<p>A única diferença que sobrou de pé nos números foi <strong>memória</strong>: Rust segurou o pico em 16 MiB onde a BEAM gastou 135. É uma vantagem real do Rust em densidade, com a ressalva de que pico de cgroup não é footprint de linguagem. Em vazão, que era a alegação da thread, empate técnico.</p>
<p>E o empate merece a leitura honesta: ele pode significar só que a minha pressão foi pouca. Com o coletor compartilhado segurando tudo em torno de 95 efeitos por segundo, qualquer stack capaz de mais que isso aparece como &ldquo;igual&rdquo;. O que esse teste mostra é onde estava o gargalo, não onde cada stack quebra. Pra achar o teto de cada uma eu precisaria do protocolo forte, com coletor de group-commit e escada de carga, que está desenhado e não executado. Até lá, &ldquo;todas as stacks performam igual&rdquo; é a conclusão errada; &ldquo;todas ficaram acima do que o meu teste conseguiu medir&rdquo; é a certa.</p>
<p>E a pergunta que interessava mais, &ldquo;o agente trabalha menos pra entregar um consumer de fila confiável em Elixir do que em Rust?&rdquo;, ficou sem resposta de propósito: essas quatro implementações foram escritas pra validar o ambiente, não como tentativa controlada de codificação. Comparar o esforço de IA entre Elixir e Rust exigiria a mesma matriz do ledger rodando na tarefa de fila. Está na lista.</p>
<h2>Afinal, a linguagem importa?<span class="hx:absolute hx:-mt-20" id="afinal-a-linguagem-importa"></span>
    <a href="#afinal-a-linguagem-importa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Com os dados na mesa, dá pra responder as três perguntas da thread:</p>
<p><strong>Existe linguagem melhor pra programar com LLM?</strong> Não apareceu nenhuma. Nos modelos de fronteira, acerto e esforço variaram menos entre linguagens do que entre modelos. Onde houve diferença de acerto, ela seguiu a presença de testes fornecidos, não a sintaxe. &ldquo;Nenhum ranking geral de linguagem se estabelece&rdquo; é a frase oficial do relatório, e é a frase correta.</p>
<p><strong>As teses de economia de token se sustentam?</strong> Nenhuma das duas. Concisão não economizou: Ruby custou tanto quanto Rust, ou mais. Tipagem forte não poupou reparo: não havia reparo de tipo pra poupar. O esforço dos modelos foi pra verificação e entendimento do contrato, e isso não tem linguagem.</p>
<p><strong>O DHH estava certo?</strong> No que ele mediu, ele mediu o que um agente entrega de graça em cada stack, sem ajuste, e chamou isso de veredito sobre linguagem. No meu teste, com carga real e medição honesta, as quatro stacks empataram em vazão no regime estável, e só a memória diferenciou o Rust. Se a thread dele prova alguma coisa, prova que conversão por agente mede o modelo, não a linguagem.</p>
<p>Minhas apostas da introdução saíram de pé: teste e disciplina são a alavanca, o compilador é coadjuvante, e a ferramenta certa depende do problema. Web app novo continua sendo Rails na minha mão, fila pesada continua sendo território Erlang, sistema baixo nível continua sendo Rust, e o LLM escreve qualquer uma delas com a mesma competência. Quem passou a semana brigando pela linguagem favorita no X brigou por nada.</p>
<p>E a minha prática diária conta a mesma história. Eu tenho viés pessoal pra Rust e mesmo assim já publiquei mais de 30 projetos open source em todo tipo de stack: o <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a> é Rust, o <a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener">FrankMD</a> é Ruby on Rails, o <a href="https://github.com/akitaonrails/ai-memory-web"target="_blank" rel="noopener">ai-memory-web</a> e o <a href="https://github.com/akitaonrails/ai-jail-web"target="_blank" rel="noopener">ai-jail-web</a> são Node.js. Em nenhum deles eu senti diferença relevante que viesse da stack: os ganhos foram parecidos, os erros também, e os erros eram sempre compreensíveis. Cada erro virou melhoria de invariante, de regra, de skill. Nunca virou troca de linguagem.</p>
<p>Então esquece &ldquo;economia de token&rdquo; e &ldquo;superstição de tipagem&rdquo;. Faz engenharia de software de verdade: escolhe a ferramenta certa pro teu problema específico, aplica técnica de engenharia correta, e qualquer agente razoável vai performar mais ou menos igual. E para de gastar tempo discutindo rage bait de guerra de linguagem na internet. Seja engenheiro de software de verdade e faça engenharia de verdade. É só isso.</p>
<p>Os dados por tentativa, a metodologia completa e os limites de evidência estão no <a href="https://github.com/akitaonrails/vibe-coding-benchmarks"target="_blank" rel="noopener">repositório</a>. Se você rodar a matriz com outra tarefa e achar um ranking que eu não achei, me mostra.</p>
]]></content:encoded><category>benchmarks-de-llm</category><category>linguagens-de-programacao</category><category>llms</category><category>elixir-e-erlang</category></item><item><title>Nova linguagem voltada pra IA acabou de ser lançada: Bend 2</title><link>https://akitaonrails.github.io/2026/09/19/nova-linguagem-voltada-pra-ia-bend-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/09/19/nova-linguagem-voltada-pra-ia-bend-2/</guid><pubDate>Sat, 19 Sep 2026 12:00:00 -0300</pubDate><description>&lt;p&gt;Anteontem, 17 de setembro, o pesquisador brasileiro Victor Taelin &lt;a href="https://x.com/VictorTaelin/status/2100681226143092875"target="_blank" rel="noopener"&gt;anunciou no X&lt;/a&gt; o lançamento do &lt;strong&gt;Bend 2&lt;/strong&gt;, hospedado em &lt;a href="https://bend-lang.com/"target="_blank" rel="noopener"&gt;bend-lang.com&lt;/a&gt;. O slogan é ambicioso: &lt;em&gt;&amp;ldquo;a fast language that blocks AI mistakes via proof — C speed · CUDA parallelism · Lean proofs · Python syntax&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Uma linguagem que promete rodar quase na velocidade de C, paralelizar sozinha em CPU e GPU, e ainda verificar com prova formal que o código que a IA escreveu faz o que você pediu. É muita promessa junta. Eu nunca tinha encostado no Bend 1, então isso aqui é experiência nova pra mim também: no dia do lançamento, apontei meus agentes pra dois projetos meus em Rust e mandei portar pra Bend 2. O resultado desse teste é a parte mais interessante deste artigo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Anteontem, 17 de setembro, o pesquisador brasileiro Victor Taelin <a href="https://x.com/VictorTaelin/status/2100681226143092875"target="_blank" rel="noopener">anunciou no X</a> o lançamento do <strong>Bend 2</strong>, hospedado em <a href="https://bend-lang.com/"target="_blank" rel="noopener">bend-lang.com</a>. O slogan é ambicioso: <em>&ldquo;a fast language that blocks AI mistakes via proof — C speed · CUDA parallelism · Lean proofs · Python syntax&rdquo;</em>.</p>
<p>Uma linguagem que promete rodar quase na velocidade de C, paralelizar sozinha em CPU e GPU, e ainda verificar com prova formal que o código que a IA escreveu faz o que você pediu. É muita promessa junta. Eu nunca tinha encostado no Bend 1, então isso aqui é experiência nova pra mim também: no dia do lançamento, apontei meus agentes pra dois projetos meus em Rust e mandei portar pra Bend 2. O resultado desse teste é a parte mais interessante deste artigo.</p>
<p>Antes de começar, o disclaimer: isso é uma review feita no <strong>segundo dia de vida</strong> da linguagem, baseada num experimento de um dia. Posso estar errado em várias das minhas assunções, e tá tudo bem. Os comentários estão aí embaixo, sintam-se livres pra corrigir e complementar.</p>
<h2>Quem é Victor Taelin<span class="hx:absolute hx:-mt-20" id="quem-é-victor-taelin"></span>
    <a href="#quem-%c3%a9-victor-taelin" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de tudo, vale apresentar o autor. O <a href="https://github.com/VictorTaelin"target="_blank" rel="noopener">Victor Taelin</a> é um pesquisador brasileiro de programação funcional que passou mais de uma década obcecado por um problema específico: redução ótima de lambda cálculo, o algoritmo do Lamping de 1990, e depois as redes de interação do Lafont.</p>
<p>A <a href="https://gist.github.com/VictorTaelin/77fd5a2a8a4a07e1da6157ebca3c7cf1"target="_blank" rel="noopener">própria autobiografia dele</a> conta a trajetória: trabalhou na Ethereum Foundation, criou a linguagem de provas Formality, aposentou cedo com o ETH que acumulou de salário, voltou ao problema por conta própria, criou a HVM (Higher-Order Virtual Machine) e fundou a Higher Order Company, que levantou <a href="https://x.com/VictorTaelin/status/1743751536465903795"target="_blank" rel="noopener">US$ 4 milhões de seed</a> pra transformar a pesquisa em produto.</p>
<p>E aqui vai um ponto que pra mim importa muito: <strong>eu acho ótimo um brasileiro fazendo trabalho sério de design de linguagem de programação</strong>. A gente quase não tem pesquisa de ponta de verdade nesse país, muito menos em fundamentos de computação. Só por isso, o Taelin já tem o meu respeito e os meus parabéns pelo que construiu até aqui.</p>
<p>Então deixa registrado: as críticas deste artigo são todas construtivas, de alguém que torce pro Bend chegar mais longe, nada contra ele ou contra o projeto.</p>
<h2>De onde vem o Bend<span class="hx:absolute hx:-mt-20" id="de-onde-vem-o-bend"></span>
    <a href="#de-onde-vem-o-bend" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A primeira versão do Bend apareceu em <a href="https://news.ycombinator.com/item?id=40390287"target="_blank" rel="noopener">maio de 2024</a>, com sintaxe parecida com Python rodando em cima da HVM2, e a promessa de paralelismo automático: escreva código sequencial, o runtime distribui em todos os cores e até na GPU, sem thread, sem lock, sem mutex.</p>
<p>O lançamento foi viral. Quase mil pontos no Hacker News, mais de 20 mil estrelas no GitHub, vídeo do Fireship. E aí veio a realidade: a <a href="https://news.ycombinator.com/item?id=40390287"target="_blank" rel="noopener">thread do HN</a> foi impiedosa. Números de 24 bits, sem FFI, soma simples levando 42 minutos na máquina de um usuário, C++ em um core batendo a RTX 4090 do demo. O próprio Taelin admitiu na época que o codegen era ruim, e admitiu agora, na <a href="https://news.ycombinator.com/item?id=49746163"target="_blank" rel="noopener">thread do Bend 2</a>, que o hype de 2024 não converteu em uso: <em>&ldquo;there is also my own failure into making the language actually be used, rather than just a viral moment&rdquo;</em>.</p>
<p>O Bend 2 é a resposta pra isso. E atenção: <strong>é outra linguagem</strong>. O README é explícito que programas de Bend 1 não carregam. Saiu a avaliação por grafos de interação (a HVM); entrou compilação nativa pra um único arquivo C, com clang pra CPU e Metal ou CUDA pra GPU. Os números passam a ser Nat, U32 e F32 de verdade, e o limite de memória pulou de 2 GB pra 8 TB, segundo o <a href="https://gihyo.jp/article/2026/09/bend-2"target="_blank" rel="noopener">anúncio japonês da gihyo</a>. As redes de interação continuam na arquitetura, mas, nas palavras dele, <em>&ldquo;inets live in it architecturally, but they don&rsquo;t exist at runtime&rdquo;</em>.</p>
<h2>O que o Bend 2 promete<span class="hx:absolute hx:-mt-20" id="o-que-o-bend-2-promete"></span>
    <a href="#o-que-o-bend-2-promete" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O pitch novo agora gira em torno de IA. A página do projeto vende uma visão <em>&ldquo;post-AGI&rdquo;</em>: humanos vão parar de escrever e ler código, então precisamos de um jeito sem ambiguidade de dizer pras IAs o que queremos. A solução do Bend é dividir o programa em dois arquivos:</p>
<ul>
<li><code>LAWS.bend</code>: as invariantes do sistema, escritas pelo humano.</li>
<li><code>PROOF.bend</code>: as provas dessas leis, escritas pela IA.</li>
</ul>
<p>O type checker do Bend é um verificador de provas, na linha do Lean e do Rocq, então <code>bend PROOF.bend</code> vira um portão de commit: se a prova não fecha, o código não entra. O demo do site é um joguinho com a lei <em>&ldquo;vencer é impossível&rdquo;</em> e um convite direto: <em>&ldquo;Skeptical? Try breaking the game&rdquo;</em>. O posicionamento é explícito: <strong>LAWS.bend é um AGENTS.md com respaldo matemático</strong>.</p>
<p>E como é a cara disso? A sintaxe lembra Python com tipos dependentes. Um programa completo com efeitos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">Base</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Performs effects on the CPU.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">IO</span><span class="p">(</span><span class="n">Unit</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">do</span> <span class="n">IO</span><span class="o">&lt;</span><span class="n">Unit</span><span class="o">&gt;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="p">:</span> <span class="n">String</span> <span class="o">&lt;-</span> <span class="n">IO</span><span class="o">.</span><span class="k">try</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">IO</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;USER&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">IO</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s2">&#34;Hello, &#34;</span> <span class="o">++</span> <span class="n">name</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O paralelismo é divide-and-conquer: você quebra o trabalho em dois e o runtime espalha pelos cores que encontrar. E o operador <code>!</code> despacha a função pra GPU:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Computes 2^d in parallel: a tree of d levels, one leaf per unit.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pow2</span><span class="p">(</span><span class="o">+</span><span class="n">d</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">U32</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">d</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">p</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">a</span> <span class="n">b</span> <span class="o">=</span> <span class="n">pow2</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="n">pow2</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">:</span> <span class="n">U32</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Runs pow2 on the GPU, via `!`.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">IO</span><span class="p">(</span><span class="n">Unit</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">result</span> <span class="o">=</span> <span class="n">pow2</span><span class="err">!</span><span class="p">(</span><span class="mi">20</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">IO</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="n">U32</span><span class="o">.</span><span class="n">show</span><span class="p">(</span><span class="n">result</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Repara nas diferenças pras linguagens mainstream: não existe <code>if</code> (todo branch é um <code>match</code> em <code>True</code>/<code>False</code>), números naturais se casam por padrão de sucessor (<code>1n+p</code>), e o <code>+d</code> marca o parâmetro como <strong>afim</strong>, que é o que permite a linguagem viver sem garbage collector.</p>
<p>E as provas são defs comuns. A lei declara o que vale; a prova é indução com reescritas explícitas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># CLAIM: for every nat x, x + 0 equals x.</span>
</span></span><span class="line"><span class="cl"><span class="n">law</span> <span class="n">add_zero</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="n">x</span><span class="p">:</span> <span class="n">Nat</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">Nat</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="n">x</span> <span class="p">:</span> <span class="n">Nat</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># PROOF: induction on `x`, one rewrite (`%`) per step.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">add_zero</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">x</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="o">==</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">xp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="o">%</span><span class="n">add_zero</span><span class="p">(</span><span class="n">xp</span><span class="p">)</span> <span class="p">:</span> <span class="p">{</span><span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">Nat</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">xp</span><span class="p">,</span> <span class="mi">0</span><span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">_</span> <span class="p">:</span> <span class="n">Nat</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="o">==</span><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Sem táticas, sem inferência, tudo anotado à mão. É verbose de propósito: é o preço do checker de 0,1s.</p>
<p>As outras promessas, todas com benchmark na página:</p>
<ul>
<li><strong>Roda rápido</strong>: Game of Life num M4 Max em 7,8s em um core, contra 6,78s do C. Na GPU, 0,06s, ou 124x.</li>
<li><strong>Verifica rápido</strong>: 3.200 instanciações genéricas checadas em 0,38s, contra 19,2s do Lean e 6,04s do Rocq. A ideia é que o agente possa verificar a cada edição.</li>
<li><strong>Paraleliza sozinho</strong>: operador <code>!</code> manda a função pra GPU, sem thread, sem lock, sem kernel escrito à mão.</li>
<li><strong>Sem garbage collector</strong>: tipos afins, cada closure pode ser chamada no máximo uma vez, então a memória se resolve estaticamente.</li>
</ul>
<p>Dois detalhes honestos que eu gostei de ver publicados: o próprio site avisa <em>&ldquo;Bend is still evolving. Expect bugs&rdquo;</em>, e o Taelin declarou que <a href="https://gihyo.jp/article/2026/09/bend-2"target="_blank" rel="noopener">o compilador é 99% escrito por IA</a>, com só o kernel de checagem auditado por humanos. Coerente com a tese dele, pelo menos.</p>
<p>E já existe um plano de negócio: o <a href="https://bend-lang.com/bender"target="_blank" rel="noopener">Bender</a>, um <em>&ldquo;proving agent&rdquo;</em> pago e proprietário, harness em cima dos modelos da Anthropic e da OpenAI hoje, com modelo dedicado e provador simbólico próprio no roadmap.</p>
<h2>Nem todo mundo engoliu os benchmarks<span class="hx:absolute hx:-mt-20" id="nem-todo-mundo-engoliu-os-benchmarks"></span>
    <a href="#nem-todo-mundo-engoliu-os-benchmarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A recepção técnica teve ressalvas fortes, e elas importam. O gist do Nezk, <a href="https://gist.github.com/Nezk/dda0511c492cf9bd673885f0341dca0e"target="_blank" rel="noopener">&ldquo;Why the benchmarks of Bend&rsquo;s 2 typechecker are misleading (and possible problems with its type system)&rdquo;</a>, aponta que o Bend pula elaboração, unificação e argumentos implícitos, que é justamente onde Lean e Rocq gastam tempo. Comparar os tempos assim seria comparar maçã com foguete.</p>
<p>Mais pesado: o post do Liam Powell, <a href="https://blog.liampwll.com/posts/bend_vibe_coding/"target="_blank" rel="noopener">&ldquo;Bend 2 and the Vibe-Coding Trap&rdquo;</a>, nota que a expressão <em>&ldquo;formal verification&rdquo;</em> não aparece no site nem no código do Bend, refaz o demo do jogo em SPARK/Ada provando as mesmas duas propriedades automaticamente com GNATprove (<em>&ldquo;all checks proved (12 checks)&rdquo;</em>), sem as 442 linhas de prova manual, e conclui que o Bend está <em>&ldquo;decades behind the state of the art&rdquo;</em>.</p>
<p>São críticas de gente que entende do assunto e merecem resposta. Dito isso, a thread principal do Bend 2 no HN está com <a href="https://news.ycombinator.com/item?id=49746163"target="_blank" rel="noopener">mais de 600 pontos</a>, então atenção o projeto tem.</p>
<h2>O que eu testei<span class="hx:absolute hx:-mt-20" id="o-que-eu-testei"></span>
    <a href="#o-que-eu-testei" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Slogan não compila programa, então eu fui testar. Tenho um repositório <a href="https://github.com/akitaonrails/bend-tests"target="_blank" rel="noopener"><code>bend-tests</code></a> onde apontei meus agentes pra portar projetos reais meus, escritos em Rust, pra Bend 2 (v2.0.16, pinada via mise; pra ter noção da maturidade, a linguagem teve nove releases em dez horas no GitHub, no dia seguinte ao lançamento). A documentação do experimento inteiro está em <code>docs/</code> lá dentro, e é dela que saem as próximas seções.</p>
<h3>Os três candidatos<span class="hx:absolute hx:-mt-20" id="os-três-candidatos"></span>
    <a href="#os-tr%c3%aas-candidatos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Escolhi três projetos meus com perfis diferentes. Já aviso: nenhum dos três é um exemplo digno de Bend, e eu sei disso. Eu só peguei projetos pequenos e simples que eu tinha à mão, meio aleatórios, pra ver onde a linguagem quebra quando esfregam nela código comum do dia a dia.</p>
<table>
  <thead>
      <tr>
          <th>Projeto</th>
          <th>Fit</th>
          <th>Por quê</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ghpending</td>
          <td>ruim</td>
          <td>90% HTTP, JSON e formatação; uma única função pequena que vale provar</td>
      </tr>
      <tr>
          <td>clock-tui</td>
          <td>ruim</td>
          <td>IO de terminal do começo ao fim; a aritmética é U32, impraticável de provar sem mathlib</td>
      </tr>
      <tr>
          <td>ai-jail</td>
          <td>pior</td>
          <td>namespaces, mounts, seccomp, exec: quase tudo syscall</td>
      </tr>
  </tbody>
</table>
<p>O <strong>ghpending</strong> é minha CLI que lista issues e PRs abertos dos meus repositórios via GraphQL. O port cobre o comando principal, os filtros, a ordenação e o config.toml, com saída verificada <strong>byte a byte idêntica</strong> ao binário Rust nos casos testados contra a API real do GitHub (no meu config cheio, a única diferença é a fork view que o port deixou de fora).</p>
<p>O <strong>tclock</strong> é meu fork do clock-tui, relógio de terminal com timer e cronômetro. O port cobre relógio, timer e stopwatch, sem o sistema de widgets (que sozinho é 2.075 linhas do Rust), verificado dirigindo o binário via pty.</p>
<p>O <strong>ai-jail</strong>, meu sandbox pra agentes de IA (bubblewrap, Landlock, seccomp), nem virou código: a avaliação no papel já matou. Um port seria um programa em C com um <code>main</code> em Bend, o que anula o propósito. E olha que ele tinha a parte mais interessante pra provar: a lógica de política, tipo <em>&ldquo;resolução de path nunca escapa da raiz&rdquo;</em>. Mas não tem como embutir isso no binário Rust, então ficou pra uma próxima.</p>
<h3>Os números<span class="hx:absolute hx:-mt-20" id="os-números"></span>
    <a href="#os-n%c3%bameros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Medi Rust e Bend lado a lado nos dois ports:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>Rust</th>
          <th>Bend</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ghpending, linhas de código</td>
          <td>1.962 (sem testes)</td>
          <td>1.068 Bend + 93 leis/provas + 109 C + 25 JS</td>
      </tr>
      <tr>
          <td>ghpending, dependências</td>
          <td>278 crates no lockfile</td>
          <td>libcurl dinâmico, mais nada</td>
      </tr>
      <tr>
          <td>ghpending, binário</td>
          <td>6,8 MB</td>
          <td>1,4 MB</td>
      </tr>
      <tr>
          <td>ghpending, build limpo</td>
          <td>57s</td>
          <td>62s (0,1s de check; o resto é clang -O3 no C gerado)</td>
      </tr>
      <tr>
          <td>ghpending, run com 43 repos</td>
          <td>3,9s wall / 16ms CPU</td>
          <td>2,1s wall / 28ms CPU</td>
      </tr>
      <tr>
          <td>tclock, binário</td>
          <td>6,7 MB</td>
          <td>115 KB</td>
      </tr>
      <tr>
          <td>tclock, idle 5s</td>
          <td>333ms CPU, 10 MB</td>
          <td>9ms CPU, 6,6 MB</td>
      </tr>
  </tbody>
</table>
<p>Lê essa tabela com cuidado, porque ela engana. As runs são bound de rede. O gap dos 43 repos é o Rust fazendo mais trabalho (fork lookups, query sequencial de viewer). O tclock do Bend redesenha frames inteiros, 26 KB/s contra 2 KB/s de diffs do ratatui. E a contagem menor de linhas é em parte porque o port faz menos. <strong>Nenhum número aqui mostra o Bend mais rápido que o Rust no mesmo trabalho.</strong></p>
<h3>As provas na prática<span class="hx:absolute hx:-mt-20" id="as-provas-na-prática"></span>
    <a href="#as-provas-na-pr%c3%a1tica" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui mora a parte que o Rust não consegue imitar, então vale o detalhe. O design que funcionou foi <strong>provar uma guarda isolada, deixando o algoritmo fora da prova</strong>. O alocador de <code>--limit</code> do ghpending ficou em dois estágios: o primeiro é o algoritmo original em U32, não provado; o segundo é uma guarda em Nat que recebe a proposta do primeiro e corta qualquer excesso. As leis dizem que o orçamento é conservado e que nenhum repo recebe mais itens do que tem.</p>
<p>Essa é a guarda de verdade, do <code>alloc.bend</code> do port:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># the lesser of b and w</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">take</span><span class="p">(</span><span class="n">b</span><span class="p">:</span> <span class="n">Nat</span><span class="p">,</span> <span class="n">w</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Nat</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">b</span> <span class="n">w</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span> <span class="n">w0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">wp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">take</span><span class="p">(</span><span class="n">bp</span><span class="p">,</span> <span class="n">wp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># what is left of b once w is taken from it</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rest</span><span class="p">(</span><span class="n">b</span><span class="p">:</span> <span class="n">Nat</span><span class="p">,</span> <span class="n">w</span><span class="p">:</span> <span class="n">Nat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Nat</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">match</span> <span class="n">b</span> <span class="n">w</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">0</span><span class="n">n</span> <span class="n">w0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span><span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">0</span><span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">bp</span> <span class="mi">1</span><span class="n">n</span><span class="o">+</span><span class="n">wp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">rest</span><span class="p">(</span><span class="n">bp</span><span class="p">,</span> <span class="n">wp</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<blockquote>
  <p>A lei vale mesmo se o estágio não provado propor lixo. A guarda não confia na aritmética que ela não consegue enxergar. Esse é o padrão que faz prova funcionar em código real.</p>

</blockquote>
<p>O teste de mutação é a medida honesta do negócio. Injetei um off-by-one na guarda que vaza orçamento: as três saídas de exemplo ficaram idênticas, então uma suíte de testes daquele tamanho teria aprovado. O portão de prova rejeitou, com o termo exato que deixou de bater. É o Bend funcionando como prometido, no detalhe.</p>
<p>Agora o preço: 93 linhas de leis e provas pra garantir dois fatos simples sobre uma guarda de umas 30 linhas, começando do zero, porque a Base não traz nenhum lema de Nat (os únicos lemas aritméticos são <code>U32.add_comm</code> e <code>Word.add_comm</code>, que não ajudam em prova de Nat; tivemos que provar associatividade da soma na mão, e o Taelin reconheceu na thread: <em>&ldquo;We need a mathlib!&rdquo;</em>).</p>
<p>E o que ficou de fora da prova: a exatidão do algoritmo (precisaria de lemas de divisão, dias de trabalho), tudo que é U32 (por baixo é um vetor de 32 bits; provar aritmética nele sem biblioteca de lemas é impraticável) e literalmente todo o resto do programa: JSON, ordenação, renderização, IO. A superfície provável de um programa como o ghpending é uns poucos por cento do código.</p>
<p>O checker, por outro lado, é a melhor parte da ferramenta: 0,1s por rodada, erro apontando uma linha exata. Foram umas 30 rodadas de check no ghpending. Pra um agente iterando em loop, é exatamente o formato de feedback que ele precisa.</p>
<h3>O preço de sair pra C<span class="hx:absolute hx:-mt-20" id="o-preço-de-sair-pra-c"></span>
    <a href="#o-pre%c3%a7o-de-sair-pra-c" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Tudo que a Base não tem vira <em>foreign effect</em>: um def em Bend cujo corpo é uma função C (pro binário nativo) e uma gêmea em JS (pro runner de desenvolvimento). Os dois ports precisaram de <strong>oito</strong> efeitos escritos à mão:</p>
<ul>
<li><code>Http.post</code>: porque não existe TLS, HTTP ou DNS (o demo oficial de HTTP hard-coda um IP).</li>
<li><code>Clock.unix</code> e <code>Clock.local</code>: porque <code>IO.now</code> é milissegundos desde o boot, sem hora de parede e sem fuso.</li>
<li><code>Tty.width</code>, <code>Tty.raw</code>, <code>Tty.key</code>, <code>Tty.size</code>: porque não existe stdin, raw mode nem consulta de terminal. Nada disso existe.</li>
<li><code>Clock.millis</code>: conveniência.</li>
</ul>
<p>E o guia oficial é claro sobre o compromisso: <strong>não há promessa de ABI</strong>. Os nomes são internos do runtime e qualquer release pode renomeá-los, então <em>&ldquo;rebuild your effects with every update&rdquo;</em>. Numa linguagem com nove releases em dez horas, boa sorte. Detalhes que completam o quadro: não existe flag pra linkar biblioteca extra (o ghpending usa um wrapper de <code>$CC</code> que injeta <code>-lcurl</code>), tipos de handle customizados são WONTFIX, e incluir header de C no meio do arquivo funcionou por sorte, porque o runtime define nomes curtos tipo <code>lock</code> e <code>u32</code>.</p>
<p>Aqui encaixa o ponto conceitual: <strong>no momento em que você cruza a fronteira do FFI, a prova ficou pra trás</strong>. O verificador garante o que está dentro do modelo dele; o código C do outro lado é ato de fé. O Rust tem exatamente o mesmo problema com <code>unsafe</code>, onde as garantias do borrow checker valem até a borda do bloco e dentro dele a prova é sua. Se você quer garantia forte de ponta a ponta, a fronteira com C precisa ser mínima ou inexistente, e hoje em Bend ela é enorme pra qualquer programa real.</p>
<h3>Onde o Bend encaixa<span class="hx:absolute hx:-mt-20" id="onde-o-bend-encaixa"></span>
    <a href="#onde-o-bend-encaixa" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Cruzando os três projetos com o que a linguagem oferece, o formato que ganha é <strong>um núcleo puro pequeno, com spec afiada que testes cobrem mal, dentro de uma casca fina de efeitos</strong>:</p>
<ul>
<li>alocadores, schedulers, rate limiters, lógica de quota</li>
<li>regras determinísticas de jogo ou simulação, principalmente com rollback netcode (o único domínio onde a Base já tem o necessário: UDP, janela, áudio)</li>
<li>máquinas de estado de consenso, ledger e matching engine</li>
<li>parsers e codecs com lei de round-trip</li>
<li>funções de merge de CRDT (comutativas, associativas, idempotentes)</li>
<li>avaliadores de política de acesso (tipo o do ai-jail)</li>
</ul>
<p>E o centro de design que os autores declaram: <strong>a IA escreve o código e as provas, o humano escreve só o LAWS.bend, e o checker arbitra</strong>. A descrição honesta mais curta que eu achei: uma <em>proof-carrying language for AI-written kernels</em>. Comparado com Lean ou Coq, que provam bem e entregam aplicação mal, o Bend é muito mais executável.</p>
<p>Uma coisa que me surpreendeu nos próprios números: de primeira eu assumiria que o nicho natural era pesquisa acadêmica em matemática. É o contrário. Pra matemática ele perde feio pra Lean 4, Coq e Isabelle: sem táticas, quase sem inferência, sem nada na escala da mathlib, sem reais, e com uma fundação (<code>Type : Type</code>, sem positivity check) que deixaria matemático de cabelo em pé. O que ele faz é <strong>verificação de software</strong>, mais perto de Dafny, F* ou Liquid Haskell do que de assistente de prova.</p>
<p>Duas limitações pesam até nesse nicho: o Bend emite um programa inteiro em vez de uma biblioteca, então o núcleo provado não pode ser linkado numa aplicação Rust hoje; e o custo de prova sem biblioteca de lemas é alto.</p>
<h3>O que mudaria o veredito<span class="hx:absolute hx:-mt-20" id="o-que-mudaria-o-veredito"></span>
    <a href="#o-que-mudaria-o-veredito" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Registrado no <code>use-cases.md</code>, a lista do que me faria reavaliar:</p>
<ul>
<li>saída de biblioteca com ABI C, pra um núcleo provado viver dentro de um programa Rust ou C</li>
<li>U64/I64 e F64</li>
<li>uma biblioteca de lemas pra Nat, listas e ordem</li>
<li>stdin, subprocessos, TLS e JSON na Base, ou um hub de pacotes que tenha isso</li>
<li>builds nativos incrementais ou mais rápidos (62s pra 1.000 linhas hoje)</li>
<li>uma ABI de efeitos que sobreviva a um release</li>
</ul>
<h2>A realidade nua e crua<span class="hx:absolute hx:-mt-20" id="a-realidade-nua-e-crua"></span>
    <a href="#a-realidade-nua-e-crua" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora a minha leitura, e você já sabe que eu não adoço.</p>
<p><strong>É uma linguagem recém-nascida.</strong> Foi lançada anteontem. Fora o repo da própria linguagem, uma busca no GitHub acha hoje só algumas dezenas de spikes de 48 horas, quase todos com zero estrelas, nada que pareça projeto de verdade. As 21 mil estrelas do repo são quase todas herança do Bend 1, o repo foi renomeado. Os demos do site são todos de brinquedo: joguinho, Game of Life, pow2. São demonstrações de possibilidade, ainda sem uso real.</p>
<p><strong>A stdlib precisa crescer uma ordem de magnitude.</strong> A força de uma linguagem mora na biblioteca padrão e no ecossistema. A única exceção histórica a essa regra foi o JavaScript, e aquilo lá é uma lata de vermes que não cabe neste artigo.</p>
<p><strong>Falta a killer app.</strong> Toda linguagem que vingou tinha uma. Os meus testes apontam o nicho (núcleos puros provados, escritos por IA), mas alguém precisa transformar isso num app que o mundo queira usar. E aqui entra uma sugestão concreta, de graça: o Taelin podia escolher software open source popular que ele acha que ficaria melhor em Bend e reescrever. O Taelin é o exato oposto do mantenedor teimoso que torce o nariz pra IA: ele desenhou a linguagem inteira em volta de IA. Então que use isso a favor: pega um ripgrep, um jq, um sqlite da vida, reescreve com IA em Bend e publica a comparação lado a lado. Performance, linhas de código, e principalmente o que as provas garantem que o original não garante. Um before/after assim no mundo real vale mais que mil demos de joguinho. E de brinde, cada rewrite desses devolve o feedback mais valioso que existe: quais buracos da stdlib doeram de verdade num projeto real. Nada prioriza melhor o que implementar na próxima versão do que código de verdade esbarrando no que falta.</p>
<p><strong>Não espere webapp, mobile ou qualquer coisa do dia a dia tão cedo.</strong> Talvez nem seja o propósito, e tudo bem também. Mas a promessa de <em>&ldquo;velocidade de C&rdquo;</em> corta pra outro lado: se o diferencial é performance, é mais fácil ir direto pro C, ainda mais hoje, com LLM escrevendo o código por você.</p>
<p>E aqui entra a minha situação concreta. Eu uso Rust pra caramba. <strong>Odeio</strong> a sintaxe, acho a ergonomia sofrível. Mas como quem escreve o código é o LLM e eu não preciso lidar com aquilo na mão, eu tolero, e em troca levo performance, garantias boas o suficiente e um ecossistema gigantesco de crates e frameworks. Ecossistema. É isso que decide.</p>
<blockquote>
  <p>Eu não escolho linguagem pela elegância do paper. Escolho pelo tamanho do ecossistema que eu não preciso escrever na mão.</p>

</blockquote>
<h2>O foco em prova e GPU, e a dúvida que fica<span class="hx:absolute hx:-mt-20" id="o-foco-em-prova-e-gpu-e-a-dúvida-que-fica"></span>
    <a href="#o-foco-em-prova-e-gpu-e-a-d%c3%bavida-que-fica" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Uma coisa que me incomodou de leve o tempo inteiro: o Taelin claramente concentra tudo em dois pilares, provas e GPU, e eu não tinha certeza do que isso significava pro escopo da linguagem. Fui ler o que ele mesmo disse no <a href="https://x.com/VictorTaelin/status/2100374221671051472"target="_blank" rel="noopener">thread de pré-lançamento</a> e no HN, e a história é mais interessante do que eu assumi.</p>
<p>O pilar GPU é a herança de pesquisa. São dez anos de interaction nets e redução ótima, da HVM até aqui. Mas tem uma nuance que me surpreendeu: o Bend 2 conscientemente <strong>trai</strong> o idealismo acadêmico dele. Nas palavras do próprio: <em>&ldquo;Bend2 is designed to be practical, not idealistic&rdquo;</em>, mais perto de C do que de Haskell, porque ele não conseguiu fazer as interaction nets baterem as variantes lower-order em hardware comum. A GPU ficou como aplicação prática da obsessão, sem virar fetiche.</p>
<p>O pilar prova é mais velho que a era LLM: vem da época da Ethereum Foundation, de contrato imutável segurando milhões com bug, que gerou o Formality. A era LLM só deu pra obsessão antiga uma roupagem de mercado: <em>&ldquo;the same capabilities that proved Navier-Stokes will now be writing real proofs that your own apps are correct&rdquo;</em>.</p>
<p>E aí vem a minha hipótese que não sobreviveu à pesquisa. Eu estava pronto pra escrever que a stdlib mínima talvez fosse <strong>de propósito</strong>, que a ideia nunca foi programação geral, e que a minha crítica ao deserto de bibliotecas estaria mirando o alvo errado. Errado eu. As palavras dele são de ambição geral mesmo: <em>&ldquo;the potential to become the most sensible choice for any vibe-coded project&rdquo;</em>, <em>&ldquo;Bend is made for the general public&rdquo;</em>, e o time <em>&ldquo;working hard to [&hellip;] make this language competitive with the mature alternatives in the market&rdquo;</em>. A única magreza deliberada é a do checker, sem inferência, verbose de propósito. A Base pequena é dia dois de vida mesmo, tanto que ele próprio pede uma mathlib.</p>
<p>Isso faz a minha crítica à stdlib morder ainda <strong>mais</strong> forte: se o objetivo declarado é ser a escolha pra qualquer projeto vibe-coded, a comparação com ecossistemas maduros é o jogo que ele mesmo escolheu jogar.</p>
<p>E tem o ângulo do negócio, que ele admite com uma franqueza rara: <em>&ldquo;we ship without a product&rdquo;</em>. A linguagem é o substrato gratuito; o vendável é o <a href="https://bend-lang.com/bender"target="_blank" rel="noopener">Bender</a> e a infraestrutura de prova que vem atrás. Vale ficar de olho nesse espaço.</p>
<p>A dúvida que sobra, e essa eu registro sem resposta: a ambição generalista é o norte dele, mas a realidade que eu medi empurra o Bend pro nicho de núcleos verificados escritos por IA. Qual dos dois vence, só o tempo diz.</p>
<h2>O recado final<span class="hx:absolute hx:-mt-20" id="o-recado-final"></span>
    <a href="#o-recado-final" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se eu pudesse dar um conselho pro Taelin, seria esse: a parte difícil, a pesquisa, ele já fez. Agora vem a parte menos glamourosa, que é a que decide se linguagem vive ou morre: <strong>marketing e construção de ecossistema</strong>. Adoção, material de ensino, killer app, stdlib, mathlib. Ir muito além de toys e demos.</p>
<p>O exemplo que me vem à cabeça é o Zig. Anos de estrada e ainda briga por adoção, mesmo tendo uso prático imediato: o <code>zig cc</code> sozinho já é um substituto trivial pra compilar C, sem usar nada da linguagem. Tem app real usando, o <a href="https://ghostty.org/"target="_blank" rel="noopener">Ghostty</a> do Mitchell Hashimoto é o caso famoso. Mas o Andrew Kelley, o autor, é uma figura notoriamente difícil, da turma que torce o nariz pra IA, e claramente não entende de construção de ecossistema, e isso segura o Zig pra trás. Perfeccionismo técnico não combina com marketing e adoção.</p>
<p>O Bend 2 tem potencial de verdade. Checker instantâneo, provas pegando bug que teste não pega, paralelismo sem cerimônia. Mas potencial sozinho não significa nada se a adoção não vier. Eu torço pra vir, e torço duplo por ser um brasileiro liderando.</p>
<p>E uma provocação final, dessa vez pra você que leu até aqui. Stdlib rala, zero frameworks, zero bibliotecas de terceiros, nenhuma killer app: tudo isso deixa de ser problema quando você olha de outro ângulo e vira mapa de oportunidade. Cada coisa que falta no Bend é uma coisa que alguém vai ter que escrever, e quem chegar primeiro grava o nome na história do projeto. Se você sempre quis contribuir com open source de impacto mas nunca achou espaço nos projetos maduros, onde já existe de tudo, aqui tem um terreno vazio esperando.</p>
<p>São os meus dois centavos. Não levem tão a sério.</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>inteligencia-artificial</category><category>rust</category></item><item><title>A Resposta sobre Bun em Rust que Andrew Kelly deveria ter feito</title><link>https://akitaonrails.github.io/2026/07/09/resposta-sobre-bun-em-rust-que-andrew-kelly-deveria-ter-feito/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/07/09/resposta-sobre-bun-em-rust-que-andrew-kelly-deveria-ter-feito/</guid><pubDate>Thu, 09 Jul 2026 17:00:00 -0300</pubDate><description>&lt;p&gt;Eu li o post oficial do Jarred Sumner, &lt;a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener"&gt;&amp;ldquo;Rewriting Bun in Rust&amp;rdquo;&lt;/a&gt;. E vou começar deixando meu viés claro: eu sou totalmente a favor da decisão do Bun de reescrever em Rust. Não porque Rust ganhou alguma guerra tribal contra Zig. Não é isso. Eu gosto muito de Zig. Mas, para o contexto do Bun, a decisão me parece compreensível, séria e bem executada.&lt;/p&gt;
&lt;p&gt;O que eu mais gostei no post do Jarred foi o nível de detalhe. Ele não vendeu a fantasia idiota de escrever um prompt tipo &amp;ldquo;rewrite Bun in Rust&amp;rdquo; e esperar milagre. Pelo contrário. O que ele descreveu foi procedimento: &lt;code&gt;PORTING.md&lt;/code&gt;, análise de lifetimes, &lt;code&gt;LIFETIMES.tsv&lt;/code&gt;, trial run com três arquivos, workflows dinâmicos, revisores adversariais, correção de processo quando os agentes começaram a fazer besteira com &lt;code&gt;git stash&lt;/code&gt;, depois compiler errors virando fila de trabalho, depois smoke tests, depois suíte inteira, depois CI multi-plataforma. Nada de vibe coding irresponsável. Engenharia usando IA como multiplicador.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu li o post oficial do Jarred Sumner, <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">&ldquo;Rewriting Bun in Rust&rdquo;</a>. E vou começar deixando meu viés claro: eu sou totalmente a favor da decisão do Bun de reescrever em Rust. Não porque Rust ganhou alguma guerra tribal contra Zig. Não é isso. Eu gosto muito de Zig. Mas, para o contexto do Bun, a decisão me parece compreensível, séria e bem executada.</p>
<p>O que eu mais gostei no post do Jarred foi o nível de detalhe. Ele não vendeu a fantasia idiota de escrever um prompt tipo &ldquo;rewrite Bun in Rust&rdquo; e esperar milagre. Pelo contrário. O que ele descreveu foi procedimento: <code>PORTING.md</code>, análise de lifetimes, <code>LIFETIMES.tsv</code>, trial run com três arquivos, workflows dinâmicos, revisores adversariais, correção de processo quando os agentes começaram a fazer besteira com <code>git stash</code>, depois compiler errors virando fila de trabalho, depois smoke tests, depois suíte inteira, depois CI multi-plataforma. Nada de vibe coding irresponsável. Engenharia usando IA como multiplicador.</p>
<p>Por isso fiquei decepcionado com a resposta do Andrew Kelley, <a href="https://andrewkelley.me/post/my-thoughts-bun-rust-rewrite.html"target="_blank" rel="noopener">&ldquo;My Thoughts on the Bun Rust Rewrite&rdquo;</a>. Andrew é o criador do Zig. Eu queria ler o melhor argumento técnico possível defendendo Zig. Queria ver ele explicar onde Bun confundiu problema de linguagem com problema de arquitetura. Queria ver quais APIs Zig poderiam ter ajudado, quais invariantes deveriam ter sido endurecidas, onde <code>comptime</code>, allocators, arenas, <code>errdefer</code>, <code>DebugAllocator</code> e o novo desenho de I/O do Zig poderiam mudar a análise.</p>
<p>Em vez disso, uma parte grande do texto vira tentativa de assassinato de reputação. Comentário sobre personalidade, gestão, VC, cultura de startup, fofoca de bastidor. Pode até haver mágoa real ali. Não me interessa. Não ajuda tecnicamente.</p>
<p>Então resolvi fazer o exercício que eu esperava ter lido: colocar Claude Fable 5 e GPT 5.6 Sol como árbitros técnicos e pedir a eles a resposta que Andrew deveria ter escrito. Não a resposta gentil. Não a resposta de torcida. A resposta técnica.</p>
<p>Antes de passar a palavra, um off-topic. Se você, como eu, ficou olhando aquelas animações bonitas do GitHub no post do Bun e pensou &ldquo;eu quero isso nos meus projetos&rdquo;, eu fui lá e fiz. O <a href="https://github.com/akitaonrails/github-visualize"target="_blank" rel="noopener"><code>github-visualize</code></a> é um dashboard self-hosted que acompanha seus repositórios e reproduz a evolução dos commits, o heatmap por hora e a corrida do CI até ficar verde. Está tudo aberto no GitHub.</p>
<p><a href="https://github.com/akitaonrails/github-visualize"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/09/bun-zig-rust/github-visualize-commit-timeline.png" alt="Timeline animada de commits no github-visualize"  loading="lazy" /></a></p>
<p>Mas agora vamos separar apoio de prova. Uma coisa é dizer &ldquo;eu teria feito algo parecido&rdquo;. Outra é dizer &ldquo;era tecnicamente impossível continuar em Zig&rdquo;. Para responder isso, precisamos olhar para causas: quais bugs eram realmente bugs de linguagem, quais eram problemas de arquitetura, quais eram custos de ecossistema, e quais poderiam ter sido reduzidos com uma disciplina diferente dentro do próprio Zig.</p>
<p>É daí que começa a análise.</p>
<p>Antes, vocabulário mínimo. <strong>Use-after-free</strong> é usar memória depois de liberá-la. <strong>Double-free</strong> é liberar a mesma memória duas vezes. <strong>Leak</strong> é esquecer de liberar. <strong>Ownership</strong> é quem é responsável por destruir um recurso. <strong>Lifetime</strong> é por quanto tempo uma referência continua válida. <strong>FFI</strong> é a fronteira onde Rust ou Zig chamam C/C++ e perdem parte das garantias da linguagem. <strong>GC root</strong> é uma forma de dizer ao coletor de lixo: &ldquo;não colete este objeto ainda&rdquo;. <strong>Pinning</strong> é segurar um buffer no lugar para ele não ser movido, destacado ou invalidado enquanto alguém ainda tem um ponteiro para ele. <strong>Reentrância</strong> é quando uma chamada volta para dentro do runtime antes da chamada anterior terminar.</p>
<h2>O que o Rust compra de verdade<span class="hx:absolute hx:-mt-20" id="o-que-o-rust-compra-de-verdade"></span>
    <a href="#o-que-o-rust-compra-de-verdade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A tese mais forte do post do Bun é simples: uma porcentagem grande dos bugs listados era use-after-free, double-free ou recurso esquecido em caminho de erro. Em Rust seguro, boa parte disso vira erro de compilador ou cleanup automático por <code>Drop</code>, desde que o recurso esteja modelado como valor dono. Rust não prova ausência geral de vazamentos: ciclos de <code>Rc</code>, <code>mem::forget</code>, <code>Box::leak</code> e <code>abort</code> ainda existem. Mas ele torna o vazamento acidental em caminho de erro muito mais difícil.</p>
<p>Isso está correto. E é um argumento forte.</p>
<p>O ponto forte do Rust aqui não é magia. É que ele obriga certas relações de posse e vida útil a aparecerem no tipo, em vez de ficarem espalhadas como convenção. Quando um valor tem dono, quando ele é movido, quando ele sai de escopo, quando um destrutor roda, isso deixa de ser só acordo entre programadores. O compilador participa.</p>
<p>No código atual do Bun, dá pra ver o padrão em miniatura. Um wrapper de bytes do MySQL guarda um valor do JavaScriptCore pinado, e o <code>Drop</code> desfaz o pin quando o objeto morre:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="nb">Drop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Bytes</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="o">!</span><span class="bp">self</span><span class="p">.</span><span class="n">pinned</span><span class="p">.</span><span class="n">is_empty</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c1">// `pinned` is rooted by the caller&#39;s MarkedArgumentBuffer for the
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c1">// lifetime of this Value (see struct doc); the FFI itself is `safe fn`.
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">JSC__JSValue__unpinArrayBuffer</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">pinned</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// self.slice dropped automatically
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L172-L180"target="_blank" rel="noopener"><code>src/sql_jsc/mysql/MySQLValue.rs</code></a>. O detalhe importante não é a chamada FFI em si. É o cleanup acompanhar o dono do valor.</p>
<p>Esse é o round que Rust ganha limpo. Em um projeto pequeno, disciplina basta. Em meio milhão de linhas, com callback reentrante, threadpool, libuv, sockets, TLS, JavaScriptCore, parser, bundler, package manager e API Node, disciplina vira custo recorrente.</p>
<p>Rust provavelmente reduziu o espaço de estados inválidos que o time precisava manter na cabeça. Vitória real.</p>
<p>Mas vitória real não significa vitória total.</p>
<h2>Zig não estava indefeso<span class="hx:absolute hx:-mt-20" id="zig-não-estava-indefeso"></span>
    <a href="#zig-n%c3%a3o-estava-indefeso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O argumento mais fraco que se pode fazer contra Zig é fingir que ele é C com sintaxe nova. Não é. Zig tem mecanismos explícitos para tratar exatamente parte desses problemas.</p>
<p>O mais óbvio é <a href="https://ziglang.org/documentation/master/#errdefer"target="_blank" rel="noopener"><code>errdefer</code></a>, o mecanismo idiomático para cleanup em caminho de erro:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="k">fn</span><span class="w"> </span><span class="nf">readThing</span><span class="p">(</span><span class="n">alloc</span><span class="p">:</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">mem</span><span class="p">.</span><span class="n">Allocator</span><span class="p">,</span><span class="w"> </span><span class="n">fail</span><span class="p">:</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="o">!</span><span class="p">[]</span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">alloc</span><span class="p">.</span><span class="nf">alloc</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="mi">16</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">errdefer</span><span class="w"> </span><span class="n">alloc</span><span class="p">.</span><span class="nf">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">error</span><span class="p">.</span><span class="n">Boom</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">buf</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">test</span><span class="w"> </span><span class="s">&#34;leak em caminho de erro&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">alloc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">testing</span><span class="p">.</span><span class="n">allocator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">testing</span><span class="p">.</span><span class="nf">expectError</span><span class="p">(</span><span class="k">error</span><span class="p">.</span><span class="n">Boom</span><span class="p">,</span><span class="w"> </span><span class="nf">readThing</span><span class="p">(</span><span class="n">alloc</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <a href="https://ziglang.org/documentation/master/#Report-Memory-Leaks"target="_blank" rel="noopener"><code>std.testing.allocator</code> reporta vazamentos</a> em testes. O <a href="https://github.com/ziglang/zig/blob/master/lib/std/heap/debug_allocator.zig"target="_blank" rel="noopener"><code>DebugAllocator</code></a> ajuda a encontrar double-free, leaks e vários usos inválidos de memória, inclusive tornando bugs mais barulhentos por não reutilizar endereços imediatamente. Isso não é a mesma coisa que prova estática de ausência de use-after-free. Não é borrow checker. Não cobre alocação feita dentro de C++, JavaScriptCore, BoringSSL ou qualquer outro allocator externo. Só pega o que passa por ele e só nos caminhos exercitados pelos testes.</p>
<p>O comentário do próprio allocator é preciso sobre o que ele entrega:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="cs">//! * Captures stack traces on allocation, free, and optionally resize.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Double free detection, which prints all three traces (first alloc, first</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//!   free, second free).</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Leak detection, with stack traces.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//! * Never reuses memory addresses, making it easier for Zig to detect branch</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cs">//!   on undefined values in case of dangling pointers.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/ziglang/zig/blob/master/lib/std/heap/debug_allocator.zig#L3-L12"target="_blank" rel="noopener"><code>debug_allocator.zig</code></a>. É ferramenta boa de teste e debug. Não é um teorema.</p>
<p>Mesmo assim, é errado dizer que Zig não tinha ferramenta nenhuma. Tinha.</p>
<p>A diferença é o momento do feedback. Em Rust, muita coisa aparece na compilação. Em Zig, muita coisa aparece em teste, fuzzing, allocator instrumentado, ASAN, revisão e estilo arquitetural. Em código crítico, isso importa. Feedback cedo é melhor.</p>
<p>Mas também importa dizer a frase inteira: se o problema era falta de invariantes claras no código Zig, a resposta poderia ter sido uma reescrita em Rust. Também poderia ter sido uma reestruturação agressiva do Zig.</p>
<p>Não sabemos qual teria sido mais barato. Só sabemos qual o Bun escolheu.</p>
<h2><code>Drop</code> não é só açúcar, mas também não é milagre<span class="hx:absolute hx:-mt-20" id="drop-não-é-só-açúcar-mas-também-não-é-milagre"></span>
    <a href="#drop-n%c3%a3o-%c3%a9-s%c3%b3-a%c3%a7%c3%bacar-mas-tamb%c3%a9m-n%c3%a3o-%c3%a9-milagre" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O post do Bun acerta ao contrastar <code>defer</code> com <code>Drop</code>.</p>
<p>Em Zig, você escreve:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">var</span><span class="w"> </span><span class="n">resource</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">Resource</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span><span class="n">allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="n">resource</span><span class="p">.</span><span class="nf">deinit</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso é explícito. Está na sua cara. Não há fluxo de controle escondido. Essa é a filosofia do Zig: se algo acontece, você deve conseguir apontar para o lugar onde acontece.</p>
<p>Em Rust, se o tipo implementa <code>Drop</code>, o cleanup acompanha o dono:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="nb">Drop</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">Resource</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="bp">self</span><span class="p">.</span><span class="n">deinit</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso não é apenas açúcar sintático. As <a href="https://doc.rust-lang.org/reference/destructors.html"target="_blank" rel="noopener">regras de destruição do Rust</a> ligam <code>Drop</code> a ownership, move semantics, ordem de destruição de campos e unwind de panic. Código seguro comum não consegue usar um valor depois de movido, nem dar double-drop no objeto como se ainda fosse dono dele. É uma garantia de linguagem.</p>
<p>Mas a garantia termina onde começa a semântica que o compilador não entende.</p>
<p>Se o <code>drop</code> chama uma função FFI para desproteger um valor do JavaScriptCore, o Rust garante que o destrutor vai rodar nos caminhos normais de escopo e drop, não em <code>abort</code>, <code>mem::forget</code>, leaks intencionais ou ciclos de <code>Rc</code>. Ele não prova que o ponteiro do JSC ainda é válido. Não prova que o GC não tem uma regra especial. Não prova que uma callback assíncrona de C não vai chamar de volta depois que você destruiu o wrapper.</p>
<p>O comentário no mesmo wrapper do Bun mostra o tamanho do contrato que ainda fica fora do compilador:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// JS could `transfer()`/detach an earlier ArrayBuffer, or drop the last
</span></span></span><span class="line"><span class="cl"><span class="sd">/// JS reference to it and force GC, while we still hold a borrowed slice
</span></span></span><span class="line"><span class="cl"><span class="sd">/// into it. Pinning the backing `ArrayBuffer` makes it non-detachable for
</span></span></span><span class="line"><span class="cl"><span class="sd">/// the duration, and the caller&#39;s stack-scoped `MarkedArgumentBuffer`
</span></span></span><span class="line"><span class="cl"><span class="sd">/// roots the wrapper so GC can&#39;t sweep the cell. `Drop` unpins.
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">Bytes</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="n">slice</span>: <span class="nc">ZigStringSlice</span><span class="p">,</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L142-L154"target="_blank" rel="noopener"><code>MySQLValue.rs</code></a>. Repara no tamanho do contrato. Ele não sumiu por ser Rust. Ficou documentado e encapsulado.</p>
<p>Essa é a fronteira real: Rust melhora o lado Rust do contrato. Ele não faz mágica dentro do heap do JavaScriptCore.</p>
<h2>A fronteira FFI continua sendo o inferno<span class="hx:absolute hx:-mt-20" id="a-fronteira-ffi-continua-sendo-o-inferno"></span>
    <a href="#a-fronteira-ffi-continua-sendo-o-inferno" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Bun não é uma CLI simples. Bun é runtime JavaScript. Ele conversa com JavaScriptCore, uWebSockets, usockets, BoringSSL, SQLite, bibliotecas de compressão, APIs de sistema, handles assíncronos, buffers que podem ser destacados por JavaScript no meio de uma coerção, callbacks reentrantes e GC conservador.</p>
<p>É aí que o debate fica interessante.</p>
<p>Dentro de um bloco <code>unsafe</code>, Rust exige disciplina. O próprio <a href="https://doc.rust-lang.org/nomicon/ffi.html"target="_blank" rel="noopener">Rustonomicon sobre FFI</a> trata chamada estrangeira e ponteiro cru como obrigações do programador. Se o binding estiver mentindo, se a ownership real não bater com o tipo, se a callback estrangeira sobreviver ao objeto Rust, o compilador não salva.</p>
<p>O binding do Bun documenta as pré-condições na própria declaração:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">unsafe</span><span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// By-value `JSValue`; C++ side null-checks and reads its own heap state.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// No caller-side preconditions → `safe fn`.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">safe</span><span class="w"> </span><span class="k">fn</span> <span class="nf">JSC__JSValue__unpinArrayBuffer</span><span class="p">(</span><span class="n">v</span>: <span class="nc">JSValue</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// 0 = detached/null, 1 = FastTypedArray (GC-movable — caller should dupe;
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="sd">/// no unpin needed), 2 = pinned ArrayBuffer (caller must unpin).
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/sql_jsc/mysql/MySQLValue.rs#L859-L868"target="_blank" rel="noopener"><code>MySQLValue.rs</code></a>. A linguagem permite declarar uma chamada estrangeira como segura. A obrigação de provar essa segurança continua sendo nossa.</p>
<p>Só que existe uma diferença importante: Rust permite encapsular esse <code>unsafe</code> atrás de APIs seguras. Você pode criar tipos de handle, <code>NonNull</code>, <code>PhantomData</code>, <code>Pin</code>, lifetimes marcando relação entre wrapper e runtime, wrappers que não implementam <code>Send</code> ou <code>Sync</code>, RAII para roots do GC, e uma superfície menor para auditoria. O objetivo não é zerar o perigo. É encolher o lugar onde o perigo mora.</p>
<p>O exemplo mais didático do post do Bun é o bug de <code>uv_close</code>. A primeira versão compilava, mas entregava um <code>Box</code> para libuv fechar assincronamente e deixava o <code>Box</code> cair no fim do escopo. Use-after-free seguido de double-free. A versão corrigida no código atual torna a transferência explícita:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">spawn</span>::<span class="n">WindowsStdioResult</span>::<span class="n">Buffer</span><span class="p">(</span><span class="n">pipe</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// `uv_close` is async — libuv keeps the raw handle pointer until the
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// next loop tick. Leak the Box so it outlives this scope; dropping it
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// here would be a use-after-free + double-free when the callback fires.
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nb">Box</span>::<span class="n">leak</span><span class="p">(</span><span class="n">pipe</span><span class="p">).</span><span class="n">close</span><span class="p">(</span><span class="n">Subprocess</span>::<span class="n">on_pipe_close</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/runtime/api/bun/js_bun_spawn_bindings.rs#L1359-L1366"target="_blank" rel="noopener"><code>js_bun_spawn_bindings.rs</code></a>. Compilou em Rust e ainda estava errado na fronteira C/async. O processo de revisão pegou.</p>
<p>Aqui <code>leak</code> não significa &ldquo;vazar para sempre&rdquo;. Significa tirar o <code>Box</code> do controle lexical do Rust e transferir a posse para o protocolo assíncrono do libuv, cuja callback depois reconstitui e libera a alocação.</p>
<p>Zig também permite escrever wrappers bons. A diferença é que ele não tem a fronteira <code>safe</code>/<code>unsafe</code> como parte da linguagem. Em Zig, tudo é explícito e revisável. Em Rust, uma parte maior do contrato pode virar tipo.</p>
<p>Para um time grande, isso pesa. Pesa ainda mais quando parte do código está sendo gerada por agentes.</p>
<h2>Arenas: sim, Bun já usava<span class="hx:absolute hx:-mt-20" id="arenas-sim-bun-já-usava"></span>
    <a href="#arenas-sim-bun-j%c3%a1-usava" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui o argumento pró-Zig precisa ser honesto. Arenas são idiomáticas e excelentes para certos problemas: parser, AST, ciclo de vida de uma requisição, dados que morrem juntos.</p>
<p>Mas o post do Bun não ignora arenas. Ele diz explicitamente que a abordagem existente já era uma mistura de lifetimes por arena, reference counting e &ldquo;pay really close attention&rdquo;. Inclusive cita parser state e AST nodes como bons casos para arena.</p>
<p>O trecho do post é esse:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Our current approach is a mix of:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">- arena lifetimes, where the scope of when it&#39;s accessible is clear
</span></span><span class="line"><span class="cl">  (parser state doesn&#39;t escape the calling function and so AST nodes
</span></span><span class="line"><span class="cl">  are a good choice there)
</span></span><span class="line"><span class="cl">- reference-counting
</span></span><span class="line"><span class="cl">- pay really close attention</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">post oficial do Bun</a>. O contra-argumento bom não é &ldquo;use arena&rdquo;. Eles já usavam. O contra-argumento bom é perguntar se as fronteiras que sobraram poderiam ter sido endurecidas em Zig.</p>
<p>Então a resposta correta não é &ldquo;vocês esqueceram arena&rdquo;. Eles não esqueceram.</p>
<p>A resposta correta é: arena resolve o caso em que o lifetime é claro. Ela não resolve sozinha handle de socket que fecha assincronamente, callback que roda depois, SSL session que precisa de <code>free</code> específico, root do GC, ponteiro que pode ser invalidado por reentrância, nem objeto que cruza várias camadas de API com ownership implícito.</p>
<p>Arena é uma ferramenta boa. Não é religião.</p>
<p>Se o Bun tivesse endurecido mais as fronteiras em Zig, talvez parte do benefício do rewrite pudesse ter vindo daí: APIs mais restritas, menos ponteiro cru atravessando subsistema, handles com ownership explícito, allocators instrumentados por padrão em teste, fuzzing direcionado para reentrância, wrappers que obrigam <code>deinit</code>, menos &ldquo;presta atenção&rdquo;.</p>
<p>Mas isso também seria uma reescrita. Só que dentro de Zig.</p>
<h2>O borrow checker ajuda, mas não onde a internet acha<span class="hx:absolute hx:-mt-20" id="o-borrow-checker-ajuda-mas-não-onde-a-internet-acha"></span>
    <a href="#o-borrow-checker-ajuda-mas-n%c3%a3o-onde-a-internet-acha" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Muita gente fala de borrow checker como se ele fosse um detector universal de bug. Não é. Borrow checker é o pedaço do compilador Rust que impede certos empréstimos inválidos: dois donos mutáveis ao mesmo tempo, referência viva depois do dono morrer, uso depois de move. É muita coisa. Não é tudo.</p>
<p>Ele não entende o heap do JavaScriptCore. Não entende todas as regras de GC. Não entende automaticamente que uma callback de C pode disparar JS, que pode destacar um <code>ArrayBuffer</code>, que pode reentrar no runtime, que pode invalidar um ponteiro capturado antes. Essas invariantes precisam ser modeladas.</p>
<p>Mas quando são modeladas, Rust cobra.</p>
<p>Um exemplo pequeno no Bun: wrapper de <code>uSockets</code> que transforma ponteiro C em borrow Rust amarrado a <code>&amp;mut self</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="w"> </span><span class="n">PacketBuffer</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_peer</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">index</span>: <span class="nc">c_int</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">sockaddr_storage</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// SAFETY: uSockets guarantees a non-null, properly-aligned peer pointer for
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// indices &lt; packet count. The returned storage lives inside the C-owned packet
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// buffer, which is exclusively loaned to the data callback for its duration; no
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// other Rust or C path holds a reference to it. The reborrow of `&amp;mut self`
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// ties the returned lifetime to this handle, so the borrow checker prevents
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// obtaining a second overlapping `&amp;mut` via `get_peer`/`get_payload`.
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="o">*</span><span class="n">us_udp_packet_buffer_peer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/uws_sys/udp.rs#L200-L209"target="_blank" rel="noopener"><code>src/uws_sys/udp.rs</code></a>. O <code>unsafe</code> ainda existe. Mas, depois que o wrapper cria a referência, o borrow checker consegue impedir certos abusos no lado Rust.</p>
<p>E Bun não é só heap JavaScript. É parser, bundler, resolver de módulos, package manager, watcher de arquivos, protocolo HTTP, buffers, caches, tabelas, filas, estado de processo, glue code. Nessas partes, ownership local importa muito. <code>Vec</code>, <code>String</code>, slices, mapas, wrappers, enums, estados finitos: tudo isso fica mais fácil de refatorar com o compilador te batendo na mão.</p>
<p>O argumento honesto não é &ldquo;borrow checker resolve Bun&rdquo;. É: o borrow checker reduz muitas classes de erro no lado Rust do Bun, e isso libera atenção humana para a parte que continua difícil.</p>
<p>É forte o suficiente. Não precisa exagerar.</p>
<h2><code>comptime</code>: aqui Zig tem uma vantagem real<span class="hx:absolute hx:-mt-20" id="comptime-aqui-zig-tem-uma-vantagem-real"></span>
    <a href="#comptime-aqui-zig-tem-uma-vantagem-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O melhor contra-exemplo técnico no próprio post do Bun é <code>Output.pretty</code>.</p>
<p>No Zig, a string de formato podia ser um parâmetro <code>comptime</code>. O próprio post do Bun coloca a diferença assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Zig:
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="n">inline</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">comptime</span><span class="w"> </span><span class="n">fmt</span>: <span class="nc">string</span><span class="p">,</span><span class="w"> </span><span class="n">args</span>: <span class="nc">anytype</span><span class="p">)</span><span class="w"> </span><span class="n">void</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Output</span><span class="p">.</span><span class="n">pretty</span><span class="p">(</span><span class="s">&#34;&lt;r&gt;{f}&lt;r&gt;&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">.{</span><span class="n">hyperlink</span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Primeira API Rust:
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">payload</span>: <span class="nc">impl</span><span class="w"> </span><span class="n">PrettyFmtInput</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Output</span>::<span class="n">pretty</span><span class="p">(</span><span class="fm">format_args!</span><span class="p">(</span><span class="s">&#34;&lt;r&gt;</span><span class="si">{}</span><span class="s">&lt;r&gt;&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">hyperlink</span><span class="p">));</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso permitia processar marcadores de cor e formatação no literal conhecido em tempo de compilação, antes de misturar argumentos runtime. Ao portar para Rust, a primeira versão virou função. Resultado: o parser de tags mexeu em bytes que vinham dos argumentos. O issue <a href="https://github.com/oven-sh/bun/issues/30693"target="_blank" rel="noopener">#30693</a> mostra a regressão no port/canary envolvendo escape OSC 8 de hyperlink e <code>&lt;r&gt;</code>, não uma falha lançada em stable.</p>
<p>O bug era exatamente a contaminação entre template e argumento runtime:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">TerminalHyperlink::Display emits an OSC 8 hyperlink:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">\x1b]8;;URL\x1b\ai\x1b]8;;\x1b\
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The closing string terminator ST is the final two bytes `\x1b\`.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is then wrapped via `Output::pretty(format_args!(&#34;&lt;r&gt;{}&lt;r&gt;&#34;, hyperlink))`.
</span></span><span class="line"><span class="cl">The rendered string tail is `...\x1b\&lt;r&gt;`.</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/issues/30693"target="_blank" rel="noopener">issue #30693</a>.</p>
<p>Depois eles corrigiram usando macro.</p>
<p>O código atual deixa a diferença explícita:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// Write to stdout with `&lt;tag&gt;` color expansion.
</span></span></span><span class="line"><span class="cl"><span class="sd">/// Function form: performs the `&lt;tag&gt;` → ANSI rewrite at runtime on the rendered
</span></span></span><span class="line"><span class="cl"><span class="sd">/// payload (using stdout&#39;s colour state). Prefer the `pretty!` macro for literal
</span></span></span><span class="line"><span class="cl"><span class="sd">/// templates so the rewrite happens at compile time.
</span></span></span><span class="line"><span class="cl"><span class="cp">#[inline(always)]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">pretty</span><span class="p">(</span><span class="n">payload</span>: <span class="nc">impl</span><span class="w"> </span><span class="n">PrettyFmtInput</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">buf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="n">into_pretty_buf</span><span class="p">(</span><span class="n">enable_ansi_colors_stdout</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">write_bytes</span><span class="p">(</span><span class="n">Destination</span>::<span class="n">Stdout</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E logo abaixo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="sd">/// Compile-time `&lt;tag&gt;` → ANSI escape rewriter.
</span></span></span><span class="line"><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="cl"><span class="sd">/// This must be a proc-macro because it consumes a string literal and emits
</span></span></span><span class="line"><span class="cl"><span class="sd">/// a new string literal usable as a `format_args!` template.
</span></span></span><span class="line"><span class="cl"><span class="sd">///
</span></span></span><span class="line"><span class="cl"><span class="sd">/// `pretty_fmt!(&#34;&lt;red&gt;{s}&lt;r&gt;&#34;, true)`  → `&#34;\x1b[31m{}\x1b[0m&#34;`
</span></span></span><span class="line"><span class="cl"><span class="sd">/// `pretty_fmt!(&#34;&lt;red&gt;{s}&lt;r&gt;&#34;, false)` → `&#34;{}&#34;`
</span></span></span><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="n">bun_core_macros</span>::<span class="n">pretty_fmt</span><span class="p">;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/bun_core/output.rs#L1765-L1805"target="_blank" rel="noopener"><code>src/bun_core/output.rs</code></a>.</p>
<p>Isso demonstra uma força legítima do Zig: <code>comptime</code> é a própria linguagem rodando em tempo de compilação. Você não muda para um sistema paralelo. Em Rust, macros são poderosas e maduras, mas são outro mecanismo. Para alguns tipos de API, Zig é simplesmente mais direto.</p>
<p>Mas também não vamos transformar um bug de port em prova universal contra Rust. O problema ali não foi &ldquo;Rust é frágil&rdquo;. Foi a tradução errada de uma API <code>comptime</code> para uma função runtime. O próprio post do Bun reconhece isso e corrige com macro.</p>
<p>A leitura correta é mais simples: Zig tinha uma garantia estrutural ali. A primeira versão Rust perdeu essa garantia. Isso custou uma regressão no port/canary.</p>
<p>Ponto para Zig.</p>
<h2>Tamanho de binário e performance: cuidado com causalidade barata<span class="hx:absolute hx:-mt-20" id="tamanho-de-binário-e-performance-cuidado-com-causalidade-barata"></span>
    <a href="#tamanho-de-bin%c3%a1rio-e-performance-cuidado-com-causalidade-barata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O post do Bun mostra números bons: binário menor, alguns benchmarks mais rápidos, startup melhor em certos cenários.</p>
<p>Os números do post:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Bun v1.4.0 (canary) — Windows — 76 MB
</span></span><span class="line"><span class="cl">Bun v1.3.14          — Windows — 94 MB
</span></span><span class="line"><span class="cl">Bun v1.4.0 (canary) — Linux   — 70 MB
</span></span><span class="line"><span class="cl">Bun v1.3.14          — Linux   — 88 MB
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Bun.serve  — 169.6k → 177.7k — +4.8%
</span></span><span class="line"><span class="cl">node:http  — 103.8k → 108.5k — +4.5%
</span></span><span class="line"><span class="cl">fastify    —  91.5k →  95.9k — +4.8%</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">post oficial do Bun</a>.</p>
<p>Eu acredito nos números. Não acredito em atribuição simples.</p>
<p>Reescrita muda muita coisa ao mesmo tempo. Muda layout de código, remove dívida, troca toolchain, mexe em LTO, muda como generics/comptime são instanciados, muda linker, muda código morto, muda ICU, muda organização interna. Quando tudo isso acontece junto, não dá para olhar o gráfico e dizer: &ldquo;foi Rust&rdquo;.</p>
<p>Também não dá para dizer: &ldquo;foi só rewrite&rdquo;.</p>
<p>O próprio post fala em combinação: Rust, mudanças em ICU, Identical Code Folding, LTO, codegen, limpeza acumulada. LTO é otimização no link final, quando o compilador enxerga mais do programa antes de gerar o binário. Identical Code Folding é o linker juntando funções idênticas em uma só. <a href="https://andrewkelley.me/post/my-thoughts-bun-rust-rewrite.html"target="_blank" rel="noopener">Andrew tem razão</a> em lembrar que Zig também tem suporte a LTO e que parte desses ganhos poderia ter sido buscada antes. Mas isso não prova que o resultado seria igual. Só prova que a causalidade é mista.</p>
<p>O <code>Cargo.toml</code> do Bun mostra que há engenharia de build explícita nessa história:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># Cargo&#39;s defaults (lto=false, codegen-units=16) leave us at ~105 crates × 16</span>
</span></span><span class="line"><span class="cl"><span class="c"># CGUs ≈ 1680 separately-optimized units with NO cross-crate inlining beyond</span>
</span></span><span class="line"><span class="cl"><span class="c"># `#[inline]`-annotated leaf fns. `lto = &#34;fat&#34;` + `codegen-units = 1` collapses</span>
</span></span><span class="line"><span class="cl"><span class="c"># the whole Rust crate graph into one LLVM module, matching Zig&#39;s shape.</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">profile</span><span class="p">.</span><span class="nx">release</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lto</span> <span class="p">=</span> <span class="s2">&#34;fat&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">codegen-units</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="nx">debug</span> <span class="p">=</span> <span class="s2">&#34;line-tables-only&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">strip</span> <span class="p">=</span> <span class="s2">&#34;none&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">panic</span> <span class="p">=</span> <span class="s2">&#34;abort&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/Cargo.toml#L117-L135"target="_blank" rel="noopener"><code>Cargo.toml</code></a>.</p>
<p>E tem configuração de LTO atravessando Rust e C++:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Cross-language LTO: rustc emits LLVM bitcode (`-Clinker-plugin-lto`) into
</span></span></span><span class="line"><span class="cl"><span class="cm"> * `libbun_rust.a` so the final lld `-flto=thin` link sees through Rust↔C++
</span></span></span><span class="line"><span class="cl"><span class="cm"> * call edges. When false but `lto` is true, both halves still LTO
</span></span></span><span class="line"><span class="cl"><span class="cm"> * independently (C++ via `-flto=thin`, Rust via `[profile.release] lto =
</span></span></span><span class="line"><span class="cl"><span class="cm"> * &#34;fat&#34;`); only the cross-language inlining is lost.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="nx">crossLangLto</span>: <span class="kt">boolean</span><span class="p">;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/scripts/build/config.ts#L118-L129"target="_blank" rel="noopener"><code>scripts/build/config.ts</code></a>. Não é &ldquo;Rust venceu&rdquo;. É linguagem, toolchain, linker, configuração e código trabalhando juntos.</p>
<p>Mesma coisa para stack usage e profundidade de parser. O post atribui melhoria ao Rust emitir melhor <a href="https://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic"target="_blank" rel="noopener"><code>llvm.lifetime.start/end</code></a> e permitir reutilização de stack slots. Detalhe de codegen? Sim. Mas detalhe de codegen entregue pelo toolchain atual conta. O próprio Zig ainda tem uma <a href="https://github.com/ziglang/zig/issues/23475"target="_blank" rel="noopener">issue aberta sobre stack allocations que não acabam no fim do escopo</a>. Para o usuário, pouco importa se a vantagem vem da linguagem pura, do compilador ou da maturidade do backend. Importa que funciona hoje.</p>
<p>O mecanismo descrito pelo Bun é este:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Rust&#39;s LLVM IR codegen emits LLVM&#39;s llvm.lifetime.start and
</span></span><span class="line"><span class="cl">llvm.lifetime.end intrinsics for stack variables when they are no longer
</span></span><span class="line"><span class="cl">in use, which lets LLVM reuse stack space slots.</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E o ticket do Zig diz, sem rodeio, que esse caso ainda não está resolvido:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Block stack allocations do not disappear at end of block scope #23475</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Também existe a solução arquitetural: parser iterativo com stack explícita na heap. Funciona em Zig e Rust. Mas é outro trabalho.</p>
<p>Leitura honesta: os números favorecem a versão Rust do Bun naquele experimento. Eles não provam superioridade geral de linguagem.</p>
<h2>Miri, crates e o mundo real<span class="hx:absolute hx:-mt-20" id="miri-crates-e-o-mundo-real"></span>
    <a href="#miri-crates-e-o-mundo-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Há vantagens de Rust que não têm equivalente direto em Zig hoje.</p>
<p>Crates é a óbvia. O ecossistema Rust é grande, maduro e prático. Para um projeto do tamanho do Bun, biblioteca boa na prateleira vale engenharia real. Zig ainda está amadurecendo nesse ponto.</p>
<p><a href="https://github.com/rust-lang/miri"target="_blank" rel="noopener"><code>Miri</code></a> também é vantagem real. Ele detecta classes de comportamento indefinido em código Rust executado: out-of-bounds, use-after-free, dados não inicializados, problemas de alinhamento, data races em certos casos. O próprio projeto documenta os limites: não é prova formal, avalia as execuções que você fornece e tem suporte restrito para FFI e APIs de plataforma. Não substitui teste. Mas para validar abstrações <code>unsafe</code> isoladas, é uma ferramenta que Zig não tem do mesmo jeito.</p>
<p>Bun não só cita Miri no abstrato. Ele está no toolchain:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">toolchain</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">channel</span> <span class="p">=</span> <span class="s2">&#34;nightly-2026-05-06&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">components</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;rust-src&#34;</span><span class="p">,</span> <span class="s2">&#34;rustfmt&#34;</span><span class="p">,</span> <span class="s2">&#34;clippy&#34;</span><span class="p">,</span> <span class="s2">&#34;miri&#34;</span><span class="p">,</span> <span class="s2">&#34;llvm-tools&#34;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/rust-toolchain.toml#L1-L24"target="_blank" rel="noopener"><code>rust-toolchain.toml</code></a>.</p>
<p>E tem teste interno explicando exatamente o uso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Run under Miri (`bun run rust:miri -p bun_ptr`): every path here walks raw
</span></span></span><span class="line"><span class="cl"><span class="c1">// pointers through `Box::into_raw` / `heap::take`, so Tree Borrows is what
</span></span></span><span class="line"><span class="cl"><span class="c1">// proves the ref/deref/destructor handoff does not alias or use-after-free.
</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://github.com/oven-sh/bun/blob/fc865b398e51de8a95ddde4bca36408b26d40884/src/ptr/ref_count.rs#L1313-L1315"target="_blank" rel="noopener"><code>src/ptr/ref_count.rs</code></a>. Não prova o programa inteiro. Exercita uma abstração perigosa sob um modelo feito para detectar comportamento indefinido nesses caminhos executados.</p>
<p>Há também contratação, tooling, documentação, IDE, cultura de revisão, CI, exemplos, gente no mercado. Isso não é detalhe político. É engenharia. Linguagem existe dentro de ecossistema.</p>
<p>Se o Bun fosse um projeto menor, com um time menor, menos pressão e menos superfície Node-compatible, talvez eu desse outro peso para isso. Mas Bun virou runtime usado por milhões de downloads mensais. A margem para &ldquo;vamos manter disciplina perfeita&rdquo; diminui.</p>
<h2>Async: Zig talvez tenha uma resposta melhor, mas ainda não é o presente do Bun<span class="hx:absolute hx:-mt-20" id="async-zig-talvez-tenha-uma-resposta-melhor-mas-ainda-não-é-o-presente-do-bun"></span>
    <a href="#async-zig-talvez-tenha-uma-resposta-melhor-mas-ainda-n%c3%a3o-%c3%a9-o-presente-do-bun" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Zig removeu async/await antigo e está <a href="https://kristoff.it/blog/zig-new-async-io/"target="_blank" rel="noopener">redesenhando I/O em torno de <code>std.Io</code></a>, passado pelo chamador de modo parecido com <code>Allocator</code>. A ideia é boa: evitar o function coloring clássico, deixar a função menos acoplada a blocking, threaded ou event-driven, e tratar I/O como capacidade explícita.</p>
<p>O formato proposto é assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">std</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">@import</span><span class="p">(</span><span class="s">&#34;std&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">Io</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">Io</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">fn</span><span class="w"> </span><span class="nf">saveData</span><span class="p">(</span><span class="n">io</span><span class="p">:</span><span class="w"> </span><span class="n">Io</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kr">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">)</span><span class="w"> </span><span class="o">!</span><span class="kt">void</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">Io</span><span class="p">.</span><span class="n">Dir</span><span class="p">.</span><span class="nf">cwd</span><span class="p">().</span><span class="nf">createFile</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;save.txt&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">.{});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="n">file</span><span class="p">.</span><span class="nf">close</span><span class="p">(</span><span class="n">io</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">file</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kr">const</span><span class="w"> </span><span class="n">out</span><span class="p">:</span><span class="w"> </span><span class="n">Io</span><span class="p">.</span><span class="n">File</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="nf">stdout</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="nf">writeAll</span><span class="p">(</span><span class="n">io</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;save complete&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://kristoff.it/blog/zig-new-async-io/"target="_blank" rel="noopener">Kristoff sobre o novo async I/O do Zig</a>. O <code>io</code> é uma capacidade passada explicitamente, como <code>Allocator</code>.</p>
<p>Eu gosto muito dessa direção.</p>
<p>Mas ela ainda está em transição. A <a href="https://github.com/ziglang/zig/issues/23446"target="_blank" rel="noopener">issue de acompanhamento</a> continua aberta: há protótipo, roadmap, discussão, partes entrando aos poucos. Não é uma base estável sobre a qual o Bun pudesse apostar hoje sem custo.</p>
<p>Então o argumento aqui não pode ser &ldquo;Zig já resolveu&rdquo;. A formulação correta é: Zig está caminhando para um desenho de async/I/O que pode ser mais limpo em alguns aspectos do que o modelo colorido tradicional. Mas Bun precisava tomar decisão com o que existe agora, não com o que promete existir depois.</p>
<p>Isso é sempre cruel com linguagens jovens. Mas é real.</p>
<h2>O processo do rewrite importa mais que a linguagem<span class="hx:absolute hx:-mt-20" id="o-processo-do-rewrite-importa-mais-que-a-linguagem"></span>
    <a href="#o-processo-do-rewrite-importa-mais-que-a-linguagem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O melhor do post do Jarred não é Rust. É método.</p>
<p>O loop mental do post é literalmente isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// Pseudocode, not real code:
</span></span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">((</span><span class="nx">task</span> <span class="o">=</span> <span class="nx">todoList</span><span class="p">.</span><span class="nx">pop</span><span class="p">()))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">task</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">feedback</span> <span class="o">=</span> <span class="kr">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span><span class="nx">review</span><span class="p">(</span><span class="nx">result</span><span class="p">),</span> <span class="nx">review</span><span class="p">(</span><span class="nx">result</span><span class="p">)]);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">apply</span><span class="p">(</span><span class="nx">feedback</span><span class="p">,</span> <span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Depois, compiler errors viraram fila:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">~16,000 compiler errors left
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">error: deref *mut EventLoop before field access
</span></span><span class="line"><span class="cl">error[E0034]: multiple applicable items in scope
</span></span><span class="line"><span class="cl">error: event_loop/EventLoopTimer.rs: port Timespec::ns from bun.zig</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E o ciclo por crate era explícito:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">- For each crate, run cargo check, group the output by file and save the errors to a file
</span></span><span class="line"><span class="cl">- Fix all the compiler errors within that crate
</span></span><span class="line"><span class="cl">- 2 adversarial reviewers for the crate&#39;s changes
</span></span><span class="line"><span class="cl">- 1 fixer applies the fixes</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Fonte: <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">post oficial do Bun</a>.</p>
<p>Ele não mandou Claude reescrever um runtime inteiro e foi dormir. Ele montou loops. Separou implementador de revisor. Usou contexto diferente para revisão adversarial. Fez trial run. Corrigiu o workflow quando os agentes começaram a se atropelar. Proibiu comandos perigosos. Dividiu worktrees. Transformou compiler errors em fila. Rodou a mesma suíte TypeScript porque a suíte não dependia da linguagem do runtime. Usou CI multi-plataforma como árbitro.</p>
<p>Essa é a parte que todo mundo deveria estudar.</p>
<p>O modelo não substituiu engenharia. O modelo executou trabalho dentro de um processo de engenharia. Quando o processo estava ruim, o resultado ficava ruim. Quando o processo melhorava, o resultado melhorava.</p>
<p>É o detalhe que a discussão tribal ignora. A história não é &ldquo;Claude reescreveu Bun&rdquo;. A história é &ldquo;um engenheiro transformou uma migração gigante em vários loops verificáveis e usou Claude para acelerar cada loop&rdquo;.</p>
<p>Muito mais interessante.</p>
<h2>Então Zig falhou?<span class="hx:absolute hx:-mt-20" id="então-zig-falhou"></span>
    <a href="#ent%c3%a3o-zig-falhou" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Não.</p>
<p>Zig não falhou por Bun ter escolhido Rust. Rust também não virou resposta universal porque Bun escolheu Rust.</p>
<p>O que a evidência pública mostra é mais chato: aquele código, naquele momento, estava caro demais para evoluir com o nível de confiança que o time queria. Rust comprou previsibilidade. Comprou feedback de compilador. Comprou ecossistema. Comprou um safe/unsafe boundary explícito. Comprou tooling. Comprou um ambiente em que agentes de IA conseguem bater em compiler errors e avançar mecanicamente.</p>
<p>Basta para justificar a decisão.</p>
<p>Mas não é suficiente para provar que Zig era tecnicamente inviável para Bun. Muitos problemas citados parecem também problemas de fronteira: ownership implícito, APIs permissivas demais, alocação sem contrato visível, recursos que cruzam camadas sem tipo carregando o dono, testes que talvez não capturavam invariantes difíceis, e o custo humano de revisar tudo isso.</p>
<p>Essas coisas poderiam ter sido atacadas em Zig. Talvez uma reestruturação agressiva do Zig tivesse resolvido muita coisa. Talvez não. Talvez custasse mais que o port. Talvez custasse menos, mas entregasse menos confiança. Não temos o contrafactual.</p>
<p>E é exatamente por isso que a conclusão precisa ser calibrada.</p>
<p>Eu apoio a decisão do Jarred como decisão de produto e engenharia. O post dele é um dos relatos mais úteis que já vi sobre rewrite grande assistido por IA. Mas gostar da decisão não me obriga a fingir que ela era a única conclusão técnica possível.</p>
<p>E tem um disclaimer importante. A <a href="https://bun.com/blog/bun-in-rust"target="_blank" rel="noopener">Anthropic comprou o Bun em dezembro de 2025</a>, e o próprio Jarred abre o post dizendo que ele e o time do Bun agora trabalham lá. A Anthropic vende Claude Code. Jarred tinha todo o incentivo institucional para usar o máximo possível de Claude e transformar essa reescrita numa demonstração pública do produto do novo empregador. Isso não prova má-fé, nem invalida um resultado técnico. Mas adiciona viés à decisão e à forma como o caso é apresentado. Ignorar esse conflito de incentivos seria tão ingênuo quanto ignorar a mágoa do Andrew com a saída do Bun de Zig.</p>
<p>A melhor leitura é menos dramática: Bun escolheu pagar um custo grande agora para reduzir custos recorrentes de manutenção depois. Rust foi uma escolha defensável, talvez ótima para aquele time naquele momento. Zig continuou sendo uma linguagem excelente, mas teria exigido outro tipo de reengenharia: mais invariantes explícitas, menos ownership informal, mais ferramentas ligadas por padrão, mais fronteiras endurecidas.</p>
<p>Andrew poderia ter escrito isso. Poderia ter defendido Zig no campo técnico. Poderia ter dito: &ldquo;Rust comprou algumas garantias reais, mas boa parte do problema era arquitetura e processo; aqui está como Zig poderia ter atacado as mesmas causas.&rdquo; Esse teria sido um post útil.</p>
<p>Não precisamos declarar vencedor. Linguagem não substitui arquitetura. Ela só muda quais erros ficam baratos de detectar e quais continuam dependendo de disciplina humana.</p>
<p>Bun escolheu trocar uma parte dessa disciplina por compilador. Eu acho uma boa aposta.</p>
<p>Não acho que isso enterre Zig.</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>rust</category><category>zig</category></item><item><title>Portando 10 Mil Linhas de Python pra Crystal com Claude: easy-subtitle</title><link>https://akitaonrails.github.io/2026/03/07/portando-10-mil-linhas-de-python-pra-crystal-com-claude-easy-subtitle/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/03/07/portando-10-mil-linhas-de-python-pra-crystal-com-claude-easy-subtitle/</guid><pubDate>Sat, 07 Mar 2026 22:00:00 -0300</pubDate><description>&lt;p&gt;No &lt;a href="https://akitaonrails.github.io/2026/03/07/crystal-e-um-wrapper-inteligente-pro-ffmpeg-feito-em-3-horas-easy-ffmpeg/"&gt;artigo anterior&lt;/a&gt; eu mostrei por que escolhi Crystal pra CLIs de linha de comando. Nesse artigo eu quero mostrar um caso mais ambicioso. Não é mais uma ferramenta do zero &amp;ndash; é um port feature-parity de um projeto open source de 10 mil linhas de Python.&lt;/p&gt;
&lt;h2&gt;O Problema: Legendas&lt;span class="hx:absolute hx:-mt-20" id="o-problema-legendas"&gt;&lt;/span&gt;
&lt;a href="#o-problema-legendas" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quem mantém uma coleção de filmes e séries sabe a dor. Você baixa o arquivo MKV, mas a legenda embutida está dessincronizada. Ou pior: não tem legenda nenhuma no idioma que você quer. Aí você vai no OpenSubtitles, baixa uma legenda, e ela tá 3 segundos adiantada porque foi feita pra outro release. O fluxo manual é:&lt;/p&gt;</description><content:encoded><![CDATA[<p>No <a href="/2026/03/07/crystal-e-um-wrapper-inteligente-pro-ffmpeg-feito-em-3-horas-easy-ffmpeg/">artigo anterior</a> eu mostrei por que escolhi Crystal pra CLIs de linha de comando. Nesse artigo eu quero mostrar um caso mais ambicioso. Não é mais uma ferramenta do zero &ndash; é um port feature-parity de um projeto open source de 10 mil linhas de Python.</p>
<h2>O Problema: Legendas<span class="hx:absolute hx:-mt-20" id="o-problema-legendas"></span>
    <a href="#o-problema-legendas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quem mantém uma coleção de filmes e séries sabe a dor. Você baixa o arquivo MKV, mas a legenda embutida está dessincronizada. Ou pior: não tem legenda nenhuma no idioma que você quer. Aí você vai no OpenSubtitles, baixa uma legenda, e ela tá 3 segundos adiantada porque foi feita pra outro release. O fluxo manual é:</p>
<ol>
<li>Extrair tracks de legenda do MKV com <code>mkvextract</code></li>
<li>Ir no OpenSubtitles, procurar legenda pro filme</li>
<li>Baixar, testar, ver que tá fora de sync</li>
<li>Rodar algum tool de sync (ffsubsync, alass)</li>
<li>Renomear o arquivo, mover pro lugar certo</li>
<li>Repetir pra cada idioma, pra cada filme</li>
</ol>
<p>Pra quem tem 10 filmes, é tolerável. Pra quem tem centenas, é insano. É exatamente o tipo de coisa que deveria ser automatizada.</p>
<h2>Subservient: A Solução Python<span class="hx:absolute hx:-mt-20" id="subservient-a-solução-python"></span>
    <a href="#subservient-a-solu%c3%a7%c3%a3o-python" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pesquisando o que já existia, encontrei o <a href="https://github.com/N3xigen/Subservient"target="_blank" rel="noopener">Subservient</a>. É um projeto Python que automatiza exatamente esse fluxo: extrai legendas de MKVs, baixa do OpenSubtitles via API REST, sincroniza com ffsubsync, e limpa propagandas dos arquivos SRT.</p>
<p>O projeto é completo. Tem modo filme e modo série, smart sync (testa todos os candidatos em paralelo e pega o melhor) e first-match (para no primeiro que funcionar). Usa o hash do OpenSubtitles pra matching exato e limpa watermarks e propagandas com mais de 30 regex patterns.</p>
<p>Mas tem os problemas típicos de distribuição Python:</p>
<ul>
<li><strong>7 dependências pip</strong>: colorama, requests, langdetect, ffsubsync, platformdirs, pycountry, tqdm</li>
<li><strong>ffsubsync como engine de sync</strong>: que por sua vez depende de numpy, auditok, e mais um monte de pacote Python</li>
<li><strong>Interface interativa com menus</strong>: boa pra uso manual, péssima pra scriptabilidade</li>
<li><strong>Config em formato INI</strong>: não é o fim do mundo, mas YAML é mais ergonômico</li>
<li><strong>10.220 linhas em 6 arquivos Python</strong>: arquivos de 2.700 linhas com funções de centenas de linhas cada</li>
</ul>
<p>O ponto não é que Python seja ruim pra isso. O Subservient funciona. Mas instalar e manter em produção é outra história. Se você quer rodar num server headless, precisa de Python 3.8+, pip, virtualenv (ou vai poluir o sistema), e torcer pra nenhuma dependência quebrar com a próxima atualização do OS.</p>
<h2>O Experimento: Port pra Crystal com Claude<span class="hx:absolute hx:-mt-20" id="o-experimento-port-pra-crystal-com-claude"></span>
    <a href="#o-experimento-port-pra-crystal-com-claude" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui é onde a coisa fica interessante. Eu queria testar uma hipótese: o Claude consegue pegar um projeto open source grande, entender a arquitetura, e fazer um port completo pra outra linguagem?</p>
<p>Não estou falando de traduzir arquivo por arquivo. Estou falando de entender o que o projeto faz, redesenhar a arquitetura onde faz sentido, e gerar código idiomático em Crystal.</p>
<p>O que eu fiz:</p>
<ol>
<li>Pedi pro Claude clonar e analisar o repositório do Subservient</li>
<li>Expliquei as decisões de design: usar <a href="https://github.com/kaegi/alass"target="_blank" rel="noopener">alass</a> (binário Rust, sem dependências Python) no lugar do ffsubsync, subcomandos CLI ao invés de menus interativos, YAML ao invés de INI</li>
<li>Pedi port feature-parity, com testes</li>
</ol>
<p>O alass é um detalhe importante. O ffsubsync funciona bem, mas é um pacote Python que puxa numpy e faz análise de áudio. O alass faz a mesma coisa (sincronização de legendas por análise de timing), mas é um binário Rust standalone. Trocar um pelo outro elimina a maior dependência Python do stack.</p>
<h2>O Resultado: easy-subtitle<span class="hx:absolute hx:-mt-20" id="o-resultado-easy-subtitle"></span>
    <a href="#o-resultado-easy-subtitle" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Cinco commits. Menos de 40 minutos do primeiro ao último.</p>
<table>
  <thead>
      <tr>
          <th>Commit</th>
          <th>Hora</th>
          <th>O que fez</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Initial implementation</td>
          <td>21:47</td>
          <td>Port completo: 42 arquivos src, 16 arquivos de teste, CI, install script</td>
      </tr>
      <tr>
          <td>Track shard.lock</td>
          <td>21:56</td>
          <td>Lock de dependências pra builds reproduzíveis</td>
      </tr>
      <tr>
          <td>Prefer ~/.local/bin</td>
          <td>22:03</td>
          <td>Fix no install script</td>
      </tr>
      <tr>
          <td>Add doctor command</td>
          <td>22:20</td>
          <td>Novo comando <code>doctor</code> pra validar setup + bump v0.2.0</td>
      </tr>
      <tr>
          <td>Homebrew formula</td>
          <td>22:24</td>
          <td>Suporte a <code>brew install</code> e workflow de auto-update</td>
      </tr>
  </tbody>
</table>
<p>O primeiro commit já entrega o projeto funcional: 8 comandos CLI, cliente OpenSubtitles com rate limiting, 76 testes passando, GitHub Actions com CI e release pra Linux e macOS.</p>
<h3>Números<span class="hx:absolute hx:-mt-20" id="números"></span>
    <a href="#n%c3%bameros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Métrica</th>
          <th>Subservient (Python)</th>
          <th>easy-subtitle (Crystal)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Código fonte</td>
          <td>10.220 linhas (6 arquivos)</td>
          <td>2.516 linhas (42 arquivos)</td>
      </tr>
      <tr>
          <td>Testes</td>
          <td>0</td>
          <td>800 linhas (76 specs)</td>
      </tr>
      <tr>
          <td>Dependências runtime</td>
          <td>7 pacotes pip + ffsubsync</td>
          <td>0 (só webmock pra testes)</td>
      </tr>
      <tr>
          <td>Binário</td>
          <td>n/a (precisa Python + deps)</td>
          <td>~6MB estático</td>
      </tr>
      <tr>
          <td>Config</td>
          <td>INI</td>
          <td>YAML</td>
      </tr>
      <tr>
          <td>Sync engine</td>
          <td>ffsubsync (Python)</td>
          <td>alass (Rust)</td>
      </tr>
      <tr>
          <td>UI</td>
          <td>Menu interativo</td>
          <td>Subcomandos CLI</td>
      </tr>
      <tr>
          <td>Concorrência</td>
          <td>ThreadPoolExecutor</td>
          <td>Crystal fibers + channels</td>
      </tr>
  </tbody>
</table>
<p>A diferença de LOC é gritante: 10.220 vs 2.516. Mas isso não é mérito só do Crystal. O Python original tem arquivos monolíticos de milhares de linhas, com muita duplicação e lógica de UI misturada com lógica de negócio. O port separa as responsabilidades em módulos pequenos e focados.</p>
<h3>Arquitetura do Port<span class="hx:absolute hx:-mt-20" id="arquitetura-do-port"></span>
    <a href="#arquitetura-do-port" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>easy-subtitle/
  src/easy_subtitle/
    cli/           # Router &#43; 9 comandos (init, extract, download, sync, run, clean, scan, hash, doctor)
    core/          # Mapa de idiomas, parser/writer/cleaner de SRT, scanner de vídeo
    acquisition/   # Cliente API OpenSubtitles, auth, busca, download, movie hash
    extraction/    # Parsing de tracks MKV, extração, remuxing
    synchronization/  # Runner do alass, cálculo de offset, estratégias smart/first-match
    models/        # VideoFile, SubtitleCandidate, CoverageEntry</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Cada módulo tem uma responsabilidade clara. O maior arquivo tem 144 linhas (config). No Python original, o <code>acquisition.py</code> sozinho tem 2.726 linhas.</p>
<h3>O Que Cada Comando Faz<span class="hx:absolute hx:-mt-20" id="o-que-cada-comando-faz"></span>
    <a href="#o-que-cada-comando-faz" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Gerar config padrão</span>
</span></span><span class="line"><span class="cl">easy-subtitle init
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Extrair legendas de dentro de MKVs</span>
</span></span><span class="line"><span class="cl">easy-subtitle extract /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Baixar legendas do OpenSubtitles</span>
</span></span><span class="line"><span class="cl">easy-subtitle download -l en,pt /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Sincronizar legendas baixadas com o vídeo</span>
</span></span><span class="line"><span class="cl">easy-subtitle sync /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Pipeline completo: extract → download → sync</span>
</span></span><span class="line"><span class="cl">easy-subtitle run /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Limpar propagandas/watermarks de SRTs</span>
</span></span><span class="line"><span class="cl">easy-subtitle clean /path/to/subtitles
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Ver cobertura de legendas por idioma</span>
</span></span><span class="line"><span class="cl">easy-subtitle scan --json /path/to/movies
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Computar hash OpenSubtitles (debug)</span>
</span></span><span class="line"><span class="cl">easy-subtitle <span class="nb">hash</span> /path/to/movie.mkv
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Validar setup: config, credenciais, dependências</span>
</span></span><span class="line"><span class="cl">easy-subtitle doctor</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>doctor</code> é um comando que eu adicionei depois. Ele verifica se o config existe, se a API key está configurada, testa login na API, e checa se <code>mkvmerge</code>, <code>mkvextract</code> e <code>alass</code> estão no PATH. Mostra instruções de instalação específicas pro OS quando algo falta.</p>
<h3>Smart Sync com Fibers<span class="hx:absolute hx:-mt-20" id="smart-sync-com-fibers"></span>
    <a href="#smart-sync-com-fibers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O smart sync é a parte que eu mais gostei de ver o port. No Python original, ele usa <code>ThreadPoolExecutor</code> pra rodar múltiplos candidatos em paralelo. Em Crystal, a mesma lógica fica mais natural com fibers e channels:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-crystal" data-lang="crystal"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">candidates</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">Path</span><span class="p">),</span> <span class="n">video</span> <span class="p">:</span> <span class="n">VideoFile</span><span class="p">)</span> <span class="p">:</span> <span class="n">SyncResult?</span>
</span></span><span class="line"><span class="cl">  <span class="n">channel</span> <span class="o">=</span> <span class="nb">Channel</span><span class="p">(</span><span class="n">SyncResult</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">candidates</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">candidate</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="bp">spawn</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">result</span> <span class="o">=</span> <span class="n">sync_one</span><span class="p">(</span><span class="n">candidate</span><span class="p">,</span> <span class="n">video</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">(</span><span class="n">SyncResult</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">candidates</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">&lt;&lt;</span> <span class="n">channel</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">accepted</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">accepted?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">accepted</span><span class="o">.</span><span class="n">min_by</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Cada candidato de legenda é sincronizado em um fiber separado (via <code>spawn</code>). Os resultados voltam pelo <code>Channel</code>. No final, pega o aceito com menor offset. Sem ThreadPoolExecutor, sem futures, sem callbacks.</p>
<h3>Rate Limiting da API<span class="hx:absolute hx:-mt-20" id="rate-limiting-da-api"></span>
    <a href="#rate-limiting-da-api" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O OpenSubtitles exige throttling de 500ms entre requests. O cliente Crystal implementa isso com Mutex:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-crystal" data-lang="crystal"><span class="line"><span class="cl"><span class="no">RATE_LIMIT_MS</span> <span class="o">=</span> <span class="mi">500</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span> <span class="k">def</span> <span class="nf">throttle!</span> <span class="p">:</span> <span class="nb">Nil</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">elapsed</span> <span class="o">=</span> <span class="nb">Time</span><span class="o">.</span><span class="n">utc</span> <span class="o">-</span> <span class="vi">@last_request_at</span>
</span></span><span class="line"><span class="cl">    <span class="n">remaining</span> <span class="o">=</span> <span class="no">RATE_LIMIT_MS</span> <span class="o">-</span> <span class="n">elapsed</span><span class="o">.</span><span class="n">total_milliseconds</span>
</span></span><span class="line"><span class="cl">    <span class="nb">sleep</span><span class="p">(</span><span class="n">remaining</span><span class="o">.</span><span class="n">milliseconds</span><span class="p">)</span> <span class="k">if</span> <span class="n">remaining</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@last_request_at</span> <span class="o">=</span> <span class="nb">Time</span><span class="o">.</span><span class="n">utc</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Simples, thread-safe, sem biblioteca externa.</p>
<h3>Instalação<span class="hx:absolute hx:-mt-20" id="instalação"></span>
    <a href="#instala%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O binário estático sai do GitHub Actions e pode ser instalado de três formas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Homebrew (macOS / Linux)</span>
</span></span><span class="line"><span class="cl">brew install akitaonrails/tap/easy-subtitle
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Script de instalação</span>
</span></span><span class="line"><span class="cl">curl -fsSL https://raw.githubusercontent.com/akitaonrails/easy-subtitle/master/install.sh <span class="p">|</span> bash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Ou baixa o binário direto dos Releases</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Um binário. Sem Python, sem pip, sem nada.</p>
<h2>Sobre Portar Coisas &ldquo;Só Porque Sim&rdquo;<span class="hx:absolute hx:-mt-20" id="sobre-portar-coisas-só-porque-sim"></span>
    <a href="#sobre-portar-coisas-s%c3%b3-porque-sim" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu sempre defendi que portar software de uma linguagem pra outra só pelo fetiche da linguagem é perda de tempo. Quantos projetos foram reescritos em Rust &ldquo;porque sim&rdquo;? Quanto esforço gasto em reescritas que não entregaram valor novo?</p>
<p>Mas preciso admitir que esse experimento me fez reconsiderar.</p>
<p>Quando o custo de portar cai de semanas/meses pra menos de 40 minutos, a equação muda. O port do Subservient pra Crystal com Claude não foi um exercício de vaidade linguística. Eu queria um binário estático que eu pudesse jogar num server e esquecer. Sem gerenciar runtime Python, sem pip install quebrando na próxima atualização do sistema.</p>
<p>E o resultado não é um port mecânico. São 2.516 linhas em 42 arquivos, contra 10.220 em 6 monolíticos. O port veio com 76 testes que o original não tinha, CI com release automático pra Linux e macOS, Homebrew formula e install script com verificação de checksum.</p>
<p>O ponto não é que Python é ruim. É que a barreira de &ldquo;será que vale a pena portar?&rdquo; ficou ridiculamente baixa. Port feature-parity com testes em menos de uma hora. Difícil argumentar contra.</p>
<p>O <a href="https://github.com/akitaonrails/easy-subtitle"target="_blank" rel="noopener">repositório está aqui</a>. GPL-3.0, como o original.</p>
]]></content:encoded><category>crystal</category><category>linguagens-de-programacao</category><category>vibe-coding</category></item><item><title>AI Agents: Qual seria a melhor Linguagem de Programação para LLMs?</title><link>https://akitaonrails.github.io/2026/02/09/ai-agents-qual-seria-a-melhor-linguagem-de-programacao-para-llms/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/02/09/ai-agents-qual-seria-a-melhor-linguagem-de-programacao-para-llms/</guid><pubDate>Mon, 09 Feb 2026 20:20:30 +0000</pubDate><description>&lt;p&gt;Resolvi fazer um exercício bem bobo: perguntar ao Claude e ao GPT Codex o que eles consideram que seriam as características de uma linguagem de programação que beneficiaria a edição por LLMs, sem se preocupar em ser amigável para programadores humanos.&lt;/p&gt;
&lt;p&gt;Design de Linguagens leva em consideração criar código razoavelmente otimizado, seguro, etc., mas balanceia isso com ser fácil para um humano ler, entender e editar depois. Normalmente essas duas coisas costumam entrar em conflito: quanto mais &amp;ldquo;amigável&amp;rdquo; pra um humano, menos eficiente é pra um compilador/máquina. Exemplos disso incluem linguagens dinâmicas como Python, Ruby, PHP e Javascript.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Resolvi fazer um exercício bem bobo: perguntar ao Claude e ao GPT Codex o que eles consideram que seriam as características de uma linguagem de programação que beneficiaria a edição por LLMs, sem se preocupar em ser amigável para programadores humanos.</p>
<p>Design de Linguagens leva em consideração criar código razoavelmente otimizado, seguro, etc., mas balanceia isso com ser fácil para um humano ler, entender e editar depois. Normalmente essas duas coisas costumam entrar em conflito: quanto mais &ldquo;amigável&rdquo; pra um humano, menos eficiente é pra um compilador/máquina. Exemplos disso incluem linguagens dinâmicas como Python, Ruby, PHP e Javascript.</p>
<p>Na minha cabeça, imagino que as &ldquo;melhores&rdquo; (sempre entre aspas) para uma máquina sejam justamente as que nós, humanos, consideramos ter piores ergonomias: linguagens funcionais, fortemente tipadas, altamente burocráticas ou &ldquo;verboses&rdquo;, por exemplo, Rust, OCaml, Lean, etc.</p>
<p>Coincidentemente (de novo, pergunte você mesmo), o Claude e o GPT chegaram mais ou menos a essa mesma conclusão. Não quer dizer que isso seja &ldquo;correto&rdquo;. É o que o modelo consegue deduzir a partir de suas probabilidades, com a informação que tem. É um enorme exercício de &ldquo;achismo&rdquo;. Mas eu acho que os argumentos fazem muito sentido.</p>
<p>Eu pedi pra me mostrar como seria um trecho de código dessa linguagem &ldquo;AI-friendly&rdquo;, que parece com a AST de um Lisp, mas com mais funcionalidades, como anotações, metadados, tipagens, etc. Eu acho que o mais próximo de uma AST — com tooling adequado pra dar feedback — seria, mais ou menos, o que uma LLM gostaria mais pra editar.</p>
<p>Vejam o que vocês acham:</p>
<h1>Update: Pact-Lang</h1><p>Na seção abaixo, há o raciocínio e as argumentações do Claude/GPT sobre a linguagem ideal. Eu gostei da ideia e resolvi pedir pro Claude gerar um protótipo de compilador que realmente funciona, usando o que ele mesmo descreveu como requisitos.</p>
<p>Ele escolheu fazer em Rust, uma linguagem chamada <strong>PACT</strong> (ele mesmo escolheu o nome), e essa versão inicial compila arquivos <code>.pct</code> e gera arquivos <code>.rs</code>. Eu nem testei ainda, só deixei ele fazer o projeto e joguei no GitHub.</p>
<ul>
<li><a href="https://github.com/akitaonrails/pact-lang"target="_blank" rel="noopener">Repositório do Pact-Lang</a></li>
</ul>
<p>No X eu comentei que não achei impressionante quando a Anthropic fez hype que o Claude conseguiu fazer um compilador de C (usando GCC como comparação). Não é difícil fazer um compilador — é uma das primeiras matérias numa boa faculdade de ciências da computação; todo mundo que estudou já fez uma.</p>
<p>E, pra reforçar este ponto, o Claude conseguiu fazer um compilador de uma linguagem que não existe, que — segundo ele — é ideal para LLMs, mas não para humanos.</p>
<p>Não sei por que ninguém discutiu essa vertente de possibilidade ainda. Fica de curiosidade.</p>
<p>Eu não acho que — pelo menos no curto prazo — isso seja algo que realmente aconteça, mas me parece inevitável: por que gastar tanto pra tentar fazer a LLM se adaptar a uma linguagem feita para humanos, se o objetivo é evitar que humanos mexam mais diretamente no código?</p>
<p>A resposta mais óbvia é voltar ao início: fazer humanos escreverem especificações de software bem detalhadas. Até hoje isso era super improdutivo; já tentamos várias vezes ao longo dos anos. Mas era impraticável escrever especificações que depois outro humano precisava converter em código: era mais inteligente criar uma linguagem mais fácil de escrever que, mais ou menos, lembrasse uma boa especificação (assim, inventamos tipos fortes, anotações, testes unitários, etc.).</p>
<p>Mas se &ldquo;gerar código&rdquo; se torna uma tarefa trivial com a LLM, vale a pena dedicar mais tempo à especificação, ao famoso &ldquo;blueprint&rdquo;. Não sei quão prático seria isso, mas é um raciocínio que, hoje, com LLMs, faz sentido, pelo menos, explorar.</p>
<p>Então vamos ao raciocínio que o Claude deu:</p>
<h1>Projetando uma Linguagem de Programação para Agentes de IA</h1><p>Um experimento mental sobre como seria uma linguagem de programação se fosse concebida para agentes de codificação baseados em LLM, e não para programadores humanos.</p>
<hr>
<h2>Sumário<span class="hx:absolute hx:-mt-20" id="sumário"></span>
    <a href="#sum%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="#o-problema">O Problema</a></li>
<li><a href="#o-que-o-design-orientado-a-humanos-faz-de-errado-para-llms">O Que o Design Orientado a Humanos Faz de Errado para LLMs</a>
<ul>
<li><a href="#salvaguardas-que-se-tornam-redundantes">Salvaguardas que se Tornam Redundantes</a></li>
<li><a href="#o-que-realmente-ajudaria-um-llm">O Que Realmente Ajudaria um LLM</a></li>
<li><a href="#o-insight-contraintuitivo">O Insight Contraintuitivo</a></li>
<li><a href="#a-pergunta-real">A Pergunta Real</a></li>
</ul>
</li>
<li><a href="#um-prototipo-como-seria">Um Protótipo: Como Seria</a>
<ul>
<li><a href="#camada-0-a-especificacao-humana">Camada 0: A Especificação Humana</a></li>
<li><a href="#camada-1-o-formato-nativo-de-ia">Camada 1: O Formato Nativo de IA</a></li>
<li><a href="#camada-2-a-projecao-humana">Camada 2: A Projeção Humana</a></li>
<li><a href="#camada-3-como-um-llm-editaria-isso">Camada 3: Como um LLM Editaria Isso</a></li>
<li><a href="#justificativa-das-decisoes-de-design">Justificativa das Decisões de Design</a></li>
<li><a href="#principal-conclusao">Principal Conclusão</a></li>
</ul>
</li>
<li><a href="#linguagens-existentes-quao-perto-estamos">Linguagens Existentes: Quão Perto Estamos?</a>
<ul>
<li><a href="#nivel-1-mais-proximas-da-visao">Nível 1: Mais Próximas da Visão</a></li>
<li><a href="#nivel-2-acertam-varias-coisas">Nível 2: Acertam Várias Coisas</a></li>
<li><a href="#nivel-3-acertam-uma-ou-duas-coisas">Nível 3: Acertam Uma ou Duas Coisas</a></li>
<li><a href="#o-padrao-desconfortavel">O Padrão Desconfortável</a></li>
</ul>
</li>
</ul>
<hr>
<h2>O Problema<span class="hx:absolute hx:-mt-20" id="o-problema"></span>
    <a href="#o-problema" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Projetamos linguagens de programação para serem rápidas, seguras e principalmente <strong>amigáveis para humanos</strong> — modularidade, isolamento de escopo, sintaxe fácil, etc. Mas quando o principal &ldquo;usuário&rdquo; de uma linguagem é um agente de codificação baseado em LLM, as restrições mudam drasticamente. Humanos têm memória de trabalho limitada, leem de forma linear e interpretam visualmente. LLMs não compartilham essas restrições — mas têm outras que as linguagens atuais ignoram.</p>
<hr>
<h2>O Que o Design Orientado a Humanos Faz de Errado para LLMs<span class="hx:absolute hx:-mt-20" id="o-que-o-design-orientado-a-humanos-faz-de-errado-para-llms"></span>
    <a href="#o-que-o-design-orientado-a-humanos-faz-de-errado-para-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Salvaguardas que se Tornam Redundantes<span class="hx:absolute hx:-mt-20" id="salvaguardas-que-se-tornam-redundantes"></span>
    <a href="#salvaguardas-que-se-tornam-redundantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>
<p><strong>Açúcar sintático</strong> — Um LLM não precisa que <code>for x in list</code> seja mais bonito do que <code>loop(list, fn(x) -&gt; ...)</code>. Ambos são igualmente fáceis de analisar. A ideia de &ldquo;sintaxe legível&rdquo; é uma preocupação humana. S-expressions de Lisp ou até mesmo notação de AST crua seriam suficientes.</p>
</li>
<li>
<p><strong>Convenções de nomes</strong> — camelCase vs snake_case, nomes curtos vs descritivos — existem para ajudar humanos a escanear e lembrar. Um LLM poderia trabalhar com UUIDs como identificadores se o <em>grafo semântico</em> estiver disponível.</p>
</li>
<li>
<p><strong>Indentação/formatação</strong> — Inteiramente visual. Delimitadores explícitos de bloco (ou apenas uma AST) são inequívocos. O whitespace significativo do Python é, na verdade, <em>mais difícil</em> para LLMs porque o espaço em branco é hostil à tokenização.</p>
</li>
<li>
<p><strong>Redução de boilerplate</strong> — DRY existe porque humanos odeiam repetição e perdem o controle de lógica duplicada. Um LLM não se cansa nem se perde. Repetição explícita com checagens de consistência garantidas pode ser <em>melhor</em> do que mágica implícita (pense em convenções do Rails que escondem comportamento).</p>
</li>
<li>
<p><strong>Divulgação progressiva de complexidade</strong> — Linguagens escondem coisas (parâmetros padrão, conversões implícitas, sobrecarga de operadores) para não sobrecarregar humanos. Isso atrapalha LLMs — comportamento oculto significa que o modelo precisa simular o que o runtime <em>realmente faz</em> versus o que o código <em>parece fazer</em>.</p>
</li>
</ul>
<h3>O Que Realmente Ajudaria um LLM<span class="hx:absolute hx:-mt-20" id="o-que-realmente-ajudaria-um-llm"></span>
    <a href="#o-que-realmente-ajudaria-um-llm" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ol>
<li>
<p><strong>Metadados semânticos ricos, inline</strong> — Não comentários (linguagem natural é ambígua), mas anotações <em>legíveis por máquina</em> de intenção. &ldquo;Esta função é pura.&rdquo; &ldquo;Este bloco deve executar em &lt; 10ms.&rdquo; &ldquo;Este invariante deve valer: x &gt; 0 na saída.&rdquo; Sistemas de tipos atuais são uma versão fraca disso. O ideal são <strong>contratos, efeitos e proveniência</strong> como cidadãos de primeira classe, não acoplados depois.</p>
</li>
<li>
<p><strong>Rastreabilidade bidirecional</strong> — Cada linha de código deve saber <em>por que existe</em>. Link para o requisito, o teste, a justificativa do commit. Ao editar uma função, a linguagem deveria dizer &ldquo;isso existe por causa do requisito R-1234, é testado pelo teste T-56 e é dependido pelos módulos A, B, C&rdquo;. Linguagens atuais não suportam isso — fica em ferramentas externas (Jira, git blame, grep).</p>
</li>
<li>
<p><strong>Grafos de dependência formais e consultáveis como primitivo da linguagem</strong> — Não <code>imports</code> para perseguir. A capacidade de perguntar ao runtime: &ldquo;qual é o impacto transitivo total de mudar este tipo?&rdquo; e obter uma resposta precisa. LSP é uma aproximação improvisada.</p>
</li>
<li>
<p><strong>Rastreamento determinístico e total de efeitos</strong> — Toda função deve declarar <em>tudo</em> o que pode fazer: I/O, mutação, alocação, panic, não-terminação. O monad IO do Haskell está na direção certa, mas é grosseiro demais. O ideal: &ldquo;esta função lê da rede, escreve nesta tabela específica do banco e pode lançar estes 3 tipos de erro&rdquo;. Isso permite que um LLM raciocine sobre mudanças <em>sem executar o código</em>.</p>
</li>
<li>
<p><strong>Semântica nativa de diff/patch</strong> — Linguagens atuais representam <em>estado</em> (o fonte atual). LLMs trabalham em <em>deltas</em> (edições). Uma linguagem desenhada para LLMs poderia representar programas como um <em>histórico de transformações</em> com significado semântico, não arquivos de texto planos. Pense: um programa é uma cadeia de refatorações, não um monte de caracteres.</p>
</li>
<li>
<p><strong>Especificações baseadas em restrições ao lado da implementação</strong> — Em vez de apenas escrever código e esperar que testes capturem erros, toda função carregaria uma especificação formal. LLMs são muito melhores em verificar &ldquo;esta implementação satisfaz esta restrição formal?&rdquo; do que &ldquo;este código faz o que aquele comentário vago diz?&rdquo;.</p>
</li>
<li>
<p><strong>Eliminação de sobrecarga ambígua</strong> — Cada operação deve ter exatamente um significado no contexto. O fato de <code>+</code> significar soma de inteiros, soma de floats, concatenação de strings e concatenação de listas dependendo dos tipos é uma conveniência humana que cria carga de inferência para LLMs.</p>
</li>
</ol>
<h3>O Insight Contraintuitivo<span class="hx:absolute hx:-mt-20" id="o-insight-contraintuitivo"></span>
    <a href="#o-insight-contraintuitivo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A linguagem que um LLM <em>realmente</em> gostaria se parece menos com Python e mais com um <strong>formato de AST tipado, total e com rastreamento de efeitos, com especificações formais embutidas e metadados completos de proveniência</strong> — basicamente um IR rico com densidade semântica de algo como Lean 4 ou Idris, mas sem qualquer preocupação com aparência na tela.</p>
<p>A ironia: <strong>isso já existe em partes.</strong> LLVM IR, WebAssembly, Typed Racket, Dafny, F*. Ninguém usa diretamente porque são hostis a humanos. Uma linguagem nativa para LLMs seria essencialmente um IR <em>muito</em> rico que nenhum humano gostaria de escrever, emparelhado com uma projeção voltada a humanos (tipo um &ldquo;view layer&rdquo;) para quando pessoas precisarem ler.</p>
<h3>A Pergunta Real<span class="hx:absolute hx:-mt-20" id="a-pergunta-real"></span>
    <a href="#a-pergunta-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A questão mais profunda não é &ldquo;que linguagem LLMs deveriam usar&rdquo; — é <strong>LLMs deveriam usar linguagens textuais?</strong> Texto é um formato de serialização para cognição humana. A interface ideal de programação para LLMs pode ser manipulação direta de um grafo semântico com verificação formal a cada passo, onde &ldquo;código-fonte&rdquo; como texto plano simplesmente não existe.</p>
<hr>
<h2>Um Protótipo: Como Seria<span class="hx:absolute hx:-mt-20" id="um-protótipo-como-seria"></span>
    <a href="#um-prot%c3%b3tipo-como-seria" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O mesmo conceito — um serviço HTTP simples de usuários — mostrado em quatro camadas: a especificação humana de intenção, o formato nativo de IA, uma projeção humana, e a interface de edição semântica.</p>
<h3>Camada 0: A Especificação Humana<span class="hx:absolute hx:-mt-20" id="camada-0-a-especificação-humana"></span>
    <a href="#camada-0-a-especifica%c3%a7%c3%a3o-humana" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se humanos não escrevem mais código — nem o formato nativo (Camada 1), nem a projeção legível (Camada 2) — o que <em>exatamente</em> eles escrevem? A resposta: <strong>uma especificação declarativa de intenção</strong>. Não é código. Não é pseudocódigo. É uma declaração estruturada do que o sistema deve fazer, quais restrições deve respeitar, e quais garantias deve oferecer. O humano descreve o <em>quê</em> e o <em>porquê</em>. A IA decide o <em>como</em>.</p>
<p>Isso não é linguagem natural livre (ambígua demais) nem uma linguagem de programação (detalhada demais). É algo intermediário: uma <strong>DSL de especificação</strong> onde o vocabulário é restrito, a estrutura é formal, mas a cognição exigida é de <em>design de produto</em>, não de <em>engenharia de software</em>.</p>
<p>Para o mesmo serviço de usuários:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w"> </span><span class="l">SPEC-2024-0042</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Serviço de usuários&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="l">time-plataforma</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="l">rascunho</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">domain</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">User</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">campos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">nome</span><span class="p">:</span><span class="w"> </span><span class="l">obrigatório, texto, 1–200 caracteres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">obrigatório, formato email, único no sistema</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">gerado automaticamente, imutável</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">endpoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">buscar-usuário</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">descrição</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Retorna um usuário pelo ID&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">entrada</span><span class="p">:</span><span class="w"> </span><span class="l">id do usuário (da URL)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">saídas</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">sucesso</span><span class="p">:</span><span class="w"> </span><span class="l">o usuário encontrado (200)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">não encontrado</span><span class="p">:</span><span class="w"> </span><span class="l">quando o ID não existe (404)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">ID inválido</span><span class="p">:</span><span class="w"> </span><span class="l">quando o formato do ID é incorreto (400)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restrições</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">tempo máximo de resposta</span><span class="p">:</span><span class="w"> </span><span class="l">50ms</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">somente leitura no banco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">criar-usuário</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">descrição</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Cria um novo usuário&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">entrada</span><span class="p">:</span><span class="w"> </span><span class="l">nome e email (do corpo da requisição, JSON)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">saídas</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">sucesso</span><span class="p">:</span><span class="w"> </span><span class="l">o usuário criado (201)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">email duplicado</span><span class="p">:</span><span class="w"> </span><span class="l">quando o email já existe (409)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">validação falhou</span><span class="p">:</span><span class="w"> </span><span class="l">lista de erros de validação (422)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restrições</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">tempo máximo de resposta</span><span class="p">:</span><span class="w"> </span><span class="l">200ms</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">idempotente por email (mesma requisição não duplica)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">escrita no banco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">qualidade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">toda função deve ser total (sem crashes, sem loops infinitos)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">todo campo deve ter validação explícita</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">todo erro deve ser tipado e enumerado (sem exceções genéricas)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">rastreabilidade</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">testes requeridos</span><span class="p">:</span><span class="w"> </span><span class="l">cobertura de todos os caminhos de saída</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">dependências conhecidas</span><span class="p">:</span><span class="w"> </span><span class="l">api-router, admin-panel</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><strong>O que essa especificação <em>não</em> contém:</strong></p>
<ul>
<li>Nenhuma decisão de implementação (qual banco, qual framework, qual padrão de validação)</li>
<li>Nenhuma sintaxe de programação (sem tipos, sem funções, sem imports)</li>
<li>Nenhum detalhe de serialização (o formato JSON é uma restrição, não uma instrução de como serializar)</li>
<li>Nenhuma preocupação com estrutura de arquivos, módulos ou organização de código</li>
</ul>
<p><strong>O que o LLM faz com isso:</strong></p>
<ol>
<li>Interpreta a especificação e gera a Camada 1 (o formato nativo de AST com todos os metadados, efeitos, invariantes e proveniência)</li>
<li>Deriva automaticamente os testes a partir das saídas declaradas</li>
<li>Calcula o grafo de dependências a partir das dependências conhecidas</li>
<li>Preenche a proveniência linkando cada função de volta à seção da spec que a originou</li>
<li>Gera a migração de schema se o tipo é novo</li>
</ol>
<p><strong>A inversão fundamental:</strong> em programação tradicional, humanos escrevem a implementação e <em>esperam</em> que ela corresponda à intenção. Aqui, humanos escrevem a intenção e o sistema <em>garante</em> que a implementação corresponda — porque a especificação é formal o suficiente para ser verificável, mas de alto nível o suficiente para ser pensada em termos de produto, não de código.</p>
<p><strong>A DSL de especificação não é YAML.</strong> O exemplo acima usa YAML como formato familiar, mas a representação real poderia ser qualquer coisa estruturada — um formulário, um grafo visual, uma interface conversacional com o agente. O que importa é que o vocabulário é restrito ao domínio (não à implementação), as restrições são explícitas (não implícitas), e a saída é <em>verificável por máquina</em> contra a implementação gerada.</p>
<p>Isso muda o papel do humano de <strong>autor de código</strong> para <strong>autor de restrições</strong>. O programador vira um <em>especificador</em> — alguém que pensa em domínio, invariantes e garantias, não em sintaxe, padrões e boilerplate.</p>
<h3>Camada 1: O Formato Nativo de IA<span class="hx:absolute hx:-mt-20" id="camada-1-o-formato-nativo-de-ia"></span>
    <a href="#camada-1-o-formato-nativo-de-ia" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso é com o que o LLM realmente trabalha:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">module</span> <span class="nv">user-service</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0042&#34;</span><span class="o">,</span> <span class="nv">author:</span> <span class="s">&#34;agent:claude-v4&#34;</span><span class="o">,</span> <span class="nv">created:</span> <span class="s">&#34;2026-02-09T14:00:00Z&#34;</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:version</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:parent-version</span> <span class="mi">6</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:delta</span> <span class="p">(</span><span class="nf">added-fn</span> <span class="nv">get-user-by-id</span> <span class="s">&#34;support single-user lookup endpoint&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">type</span> <span class="nv">User</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:invariants</span> <span class="p">[(</span><span class="nb">&gt; </span><span class="p">(</span><span class="nf">strlen</span> <span class="nv">name</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="nf">matches</span> <span class="nv">email</span> <span class="o">#</span><span class="nv">/</span><span class="o">.</span><span class="nv">+@</span><span class="o">.</span><span class="nv">+</span><span class="err">\</span><span class="o">..</span><span class="nv">+/</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">id</span>   <span class="nv">UUID</span>   <span class="nv">:immutable</span> <span class="nv">:generated</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">name</span> <span class="nv">String</span> <span class="nv">:min-len</span> <span class="mi">1</span> <span class="nv">:max-len</span> <span class="mi">200</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">field</span> <span class="nv">email</span> <span class="nv">String</span> <span class="nv">:format</span> <span class="nv">:email</span> <span class="nv">:unique-within</span> <span class="nv">user-store</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">db-read</span>    <span class="p">[</span><span class="nv">:reads</span>  <span class="nv">user-store</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">db-write</span>   <span class="p">[</span><span class="nv">:writes</span> <span class="nv">user-store</span> <span class="nv">:reads</span> <span class="nv">user-store</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">effect-set</span> <span class="nv">http-respond</span> <span class="p">[</span><span class="nv">:sends</span> <span class="nv">http-response</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">fn</span> <span class="nv">get-user-by-id</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0042#section-3&#34;</span><span class="o">,</span> <span class="nv">test:</span> <span class="p">[</span><span class="s">&#34;T-101&#34;</span> <span class="s">&#34;T-102&#34;</span> <span class="s">&#34;T-103&#34;</span><span class="p">]</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:effects</span>    <span class="p">[</span><span class="nv">db-read</span> <span class="nv">http-respond</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:total</span>      <span class="nv">true</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:latency-budget</span> <span class="mi">50</span><span class="nv">ms</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:called-by</span>  <span class="p">[</span><span class="nv">api-router/handle-request</span> <span class="nv">admin-panel/user-detail</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">param</span> <span class="nv">id</span> <span class="nv">UUID</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:source</span> <span class="nv">http-path-param</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:validated-at</span> <span class="nv">boundary</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">returns</span> <span class="p">(</span><span class="nf">union</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">ok</span>   <span class="nv">User</span>   <span class="nv">:http</span> <span class="mi">200</span> <span class="nv">:serialize</span> <span class="nv">:json</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:not-found</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">404</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:invalid-id</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">400</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">;; a lógica em si — note como é pequena em relação aos metadados</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">validated-id</span> <span class="p">(</span><span class="nf">validate-uuid</span> <span class="nv">id</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">match</span> <span class="nv">validated-id</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">err</span> <span class="nv">_</span><span class="p">)</span>    <span class="p">(</span><span class="nf">err</span> <span class="nv">:invalid-id</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">id</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">ok</span>  <span class="nv">uuid</span><span class="p">)</span> <span class="p">(</span><span class="nf">match</span> <span class="p">(</span><span class="nf">query</span> <span class="nv">user-store</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">uuid</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nf">none</span><span class="p">)</span>   <span class="p">(</span><span class="nf">err</span> <span class="nv">:not-found</span> <span class="err">{</span><span class="nv">:id</span> <span class="nv">uuid</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nf">some</span> <span class="nv">u</span><span class="p">)</span> <span class="p">(</span><span class="nf">ok</span> <span class="nv">u</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nf">fn</span> <span class="nv">create-user</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:provenance</span> <span class="err">{</span><span class="nv">req:</span> <span class="s">&#34;SPEC-2024-0041&#34;</span><span class="o">,</span> <span class="nv">test:</span> <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span><span class="p">]</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:effects</span>    <span class="p">[</span><span class="nv">db-write</span> <span class="nv">http-respond</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:total</span>      <span class="nv">true</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:idempotency-key</span> <span class="p">(</span><span class="nf">hash</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nv">:latency-budget</span> <span class="mi">200</span><span class="nv">ms</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">param</span> <span class="nv">input</span> <span class="err">{</span><span class="nv">:name</span> <span class="nv">String</span> <span class="nv">:email</span> <span class="nv">String</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:source</span> <span class="nv">http-body</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:content-type</span> <span class="nv">:json</span>
</span></span><span class="line"><span class="cl">      <span class="nv">:validated-at</span> <span class="nv">boundary</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">returns</span> <span class="p">(</span><span class="nf">union</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">ok</span>   <span class="nv">User</span>   <span class="nv">:http</span> <span class="mi">201</span> <span class="nv">:serialize</span> <span class="nv">:json</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:duplicate-email</span> <span class="err">{</span><span class="nv">:email</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">)</span><span class="err">}</span> <span class="nv">:http</span> <span class="mi">409</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nf">err</span>  <span class="nv">:validation-failed</span> <span class="p">(</span><span class="nb">list </span><span class="nv">ValidationError</span><span class="p">)</span> <span class="nv">:http</span> <span class="mi">422</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">errors</span> <span class="p">(</span><span class="nf">validate-against</span> <span class="nv">User</span> <span class="nv">input</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nf">non-empty?</span> <span class="nv">errors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">err</span> <span class="nv">:validation-failed</span> <span class="nv">errors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nf">match</span> <span class="p">(</span><span class="nf">insert!</span> <span class="nv">user-store</span> <span class="p">(</span><span class="nf">build</span> <span class="nv">User</span> <span class="nv">input</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nf">err</span> <span class="nv">:unique-violation</span><span class="p">)</span> <span class="p">(</span><span class="nf">err</span> <span class="nv">:duplicate-email</span> <span class="err">{</span><span class="nv">:email</span> <span class="p">(</span><span class="o">.</span> <span class="nv">input</span> <span class="nv">email</span><span class="p">)</span><span class="err">}</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nf">ok</span> <span class="nv">user</span><span class="p">)</span>               <span class="p">(</span><span class="nf">ok</span> <span class="nv">user</span><span class="p">))))))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Camada 2: A Projeção Humana<span class="hx:absolute hx:-mt-20" id="camada-2-a-projeção-humana"></span>
    <a href="#camada-2-a-proje%c3%a7%c3%a3o-humana" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso é auto-gerado. Não é código-fonte — é uma renderização somente leitura, como o formato de armazenamento de um banco e a saída de uma consulta. Nenhum humano escreve isso; é projetado a partir da Camada 1 sempre que alguém precisa revisar.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># --- Auto-projetado a partir de user-service v7 ---</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Spec: SPEC-2024-0042 | Tests: T-101, T-102, T-103</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@effects</span><span class="p">(</span><span class="n">reads</span><span class="o">=</span><span class="s2">&#34;user_store&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nd">@budget</span><span class="p">(</span><span class="n">latency</span><span class="o">=</span><span class="s2">&#34;50ms&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nd">@total</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">UUID</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">User</span> <span class="o">|</span> <span class="n">NotFound</span> <span class="o">|</span> <span class="n">InvalidId</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">match</span> <span class="n">validate_uuid</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="n">Err</span><span class="p">(</span><span class="n">_</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">InvalidId</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="nb">id</span><span class="p">)</span>          <span class="c1"># → 400</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="n">Ok</span><span class="p">(</span><span class="n">uuid</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">match</span> <span class="n">user_store</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">uuid</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">case</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="n">NotFound</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">uuid</span><span class="p">)</span> <span class="c1"># → 404</span>
</span></span><span class="line"><span class="cl">                <span class="k">case</span> <span class="n">user</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="n">user</span>              <span class="c1"># → 200</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Camada 3: Como um LLM Editaria Isso<span class="hx:absolute hx:-mt-20" id="camada-3-como-um-llm-editaria-isso"></span>
    <a href="#camada-3-como-um-llm-editaria-isso" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um LLM não enviaria um diff de texto. Enviaria uma <strong>operação semântica</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">edit</span> <span class="nv">user-service</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:operation</span> <span class="nv">add-field</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:target-type</span> <span class="nv">User</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:field</span> <span class="p">(</span><span class="nf">field</span> <span class="nv">role</span> <span class="p">(</span><span class="nf">enum</span> <span class="nv">:admin</span> <span class="nv">:member</span> <span class="nv">:guest</span><span class="p">)</span> <span class="nv">:default</span> <span class="nv">:member</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:reason</span> <span class="s">&#34;SPEC-2024-0055: role-based access control&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">:cascading-impacts</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; a própria linguagem calcula estes impactos e pede confirmação:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - create-user precisa aceitar parâmetro opcional &#39;role&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - get-user-by-id agora retorna &#39;role&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - 3 testes precisam ser atualizados: T-090, T-091, T-101</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - migração de schema do user-store necessária</span>
</span></span><span class="line"><span class="cl">    <span class="c1">;; - admin-panel/user-detail consome User — verificar compatibilidade</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">:confirm</span> <span class="nv">true</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O sistema responde com:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scheme" data-lang="scheme"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">edit-result</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:version</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:parent</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:changes-applied</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:changes-pending-review</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:migration-generated</span> <span class="s">&#34;M-0008-add-role-to-user&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-invalidated</span> <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span> <span class="s">&#34;T-101&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-auto-updated</span> <span class="p">[</span><span class="s">&#34;T-101&#34;</span><span class="p">]</span>    <span class="c1">;; trivial: formato de retorno mudou</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:tests-need-manual</span>  <span class="p">[</span><span class="s">&#34;T-090&#34;</span> <span class="s">&#34;T-091&#34;</span><span class="p">]</span>  <span class="c1">;; comportamental: criação mudou</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:downstream-verified</span> <span class="p">[</span><span class="s">&#34;admin-panel/user-detail: compatible&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nv">:downstream-warning</span>  <span class="p">[</span><span class="s">&#34;api-router: new field not yet exposed in list endpoint&#34;</span><span class="p">])</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Justificativa das Decisões de Design<span class="hx:absolute hx:-mt-20" id="justificativa-das-decisões-de-design"></span>
    <a href="#justificativa-das-decis%c3%b5es-de-design" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Decisão de Design</th>
          <th>Equivalente em Linguagens Humanas</th>
          <th>Por que Ajuda LLMs</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Spec declarativa de intenção (Camada 0)</td>
          <td>Doc de requisitos + user stories</td>
          <td>Entrada formal e verificável, sem ambiguidade de linguagem natural</td>
      </tr>
      <tr>
          <td>AST em s-expression</td>
          <td>blocos <code>if/else</code>, chaves</td>
          <td>Sem ambiguidade de parsing, manipulação programática trivial</td>
      </tr>
      <tr>
          <td><code>:provenance</code> em cada nó</td>
          <td>Git blame + links de Jira</td>
          <td>Nunca precisa perguntar &ldquo;por que isso existe?&rdquo; — está inline</td>
      </tr>
      <tr>
          <td>Declarações de <code>:effects</code></td>
          <td>Efeitos colaterais implícitos</td>
          <td>Sabe exatamente o que uma função toca sem ler o corpo</td>
      </tr>
      <tr>
          <td>Anotação <code>:total</code></td>
          <td>Esperança + testes</td>
          <td>&ldquo;Sem crashes, sem loops infinitos&rdquo; verificado pelo compilador</td>
      </tr>
      <tr>
          <td>Grafo <code>:called-by</code></td>
          <td>Grep por usos</td>
          <td>Análise de impacto é instantânea, não uma busca</td>
      </tr>
      <tr>
          <td><code>:latency-budget</code></td>
          <td>SLA em algum documento</td>
          <td>Restrições de performance estão no código, não no conhecimento tribal</td>
      </tr>
      <tr>
          <td>Edição semântica vs diff textual</td>
          <td><code>sed</code> / find-replace</td>
          <td>Declara intenção, o sistema calcula consequências</td>
      </tr>
      <tr>
          <td>Análise de impacto em cascata</td>
          <td>&ldquo;Você lembrou de atualizar X?&rdquo;</td>
          <td>A linguagem diz o que quebrou — sem adivinhação</td>
      </tr>
      <tr>
          <td>Tipos de retorno explícitos</td>
          <td>Exceções lançadas de qualquer lugar</td>
          <td>Todo resultado possível é enumerado — sem surpresas</td>
      </tr>
  </tbody>
</table>
<h3>Principal Conclusão<span class="hx:absolute hx:-mt-20" id="principal-conclusão"></span>
    <a href="#principal-conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A proporção de <strong>metadados para lógica</strong> é algo como 3:1. Em linguagens humanas, é o inverso. Essa é a mudança fundamental — uma linguagem nativa para IA é <em>principalmente especificação, proveniência e restrições</em> com uma camada fina de computação. A lógica é a parte fácil. Saber <strong>por quê</strong>, <strong>o que mais é afetado</strong> e <strong>que garantias devem valer</strong> é onde LLMs gastam o orçamento de raciocínio.</p>
<p>Isso não é uma linguagem para escrever código. É uma linguagem para <strong>manter sistemas</strong>. E com a Camada 0, o ciclo se completa: humanos especificam intenção, a IA gera e mantém a implementação, e a verificação formal garante que ambos concordam. O código vira um artefato intermediário — importante para a máquina, invisível para o humano.</p>
<hr>
<h2>Linguagens Existentes: Quão Perto Estamos?<span class="hx:absolute hx:-mt-20" id="linguagens-existentes-quão-perto-estamos"></span>
    <a href="#linguagens-existentes-qu%c3%a3o-perto-estamos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Um ranking honesto de linguagens populares existentes, baseado no quanto cada uma já entrega nativamente do ideal.</p>
<h3>Nível 1: Mais Próximas da Visão<span class="hx:absolute hx:-mt-20" id="nível-1-mais-próximas-da-visão"></span>
    <a href="#n%c3%advel-1-mais-pr%c3%b3ximas-da-vis%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Lean 4<span class="hx:absolute hx:-mt-20" id="lean-4"></span>
    <a href="#lean-4" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>A coisa mais próxima que existe hoje. Tipos dependentes significam que especificações <em>são</em> o código — o tipo de uma função pode literalmente dizer &ldquo;retorna uma lista ordenada cujo tamanho é igual ao da lista de entrada&rdquo;. As obrigações de prova forçam lógica total e verificada. O sistema de macros opera diretamente sobre a AST, o que se aproxima do conceito de &ldquo;edição semântica&rdquo;. O framework de metaprogramação (monad <code>Elab</code> do Lean) permite consultar e manipular o ambiente de provas programaticamente.</p>
<p><strong>Peças faltantes:</strong> sem rastreamento de efeitos, sem proveniência, sem consultas a grafos de dependência embutidas.</p>
<h4>F* (F-Star)<span class="hx:absolute hx:-mt-20" id="f-f-star"></span>
    <a href="#f-f-star" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Linguagem do Microsoft Research. Tem <em>exatamente</em> o sistema de efeitos descrito acima — você declara <code>ST</code> para estado, <code>IO</code> para I/O, <code>Pure</code> para computação pura, e o compilador impõe isso. Tipos refinados permitem codificar invariantes como <code>:min-len 1</code> diretamente no tipo. Ela pode extrair código verificado para OCaml, F# ou C. É a coisa mais próxima de &ldquo;muita especificação com pouca lógica&rdquo;.</p>
<p>Quase ninguém usa fora de pesquisa, o que diz muito sobre o trade-off de amigabilidade para humanos.</p>
<h4>Idris 2<span class="hx:absolute hx:-mt-20" id="idris-2"></span>
    <a href="#idris-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Semelhante a Lean, mas com <strong>teoria de tipos quantitativa</strong> de primeira classe — o sistema de tipos rastreia <em>quantas vezes</em> um valor é usado. Isso é uma forma primitiva de rastreamento de recursos/efeitos. A elaborator reflection permite que programas inspecionem e modifiquem o próprio processo de type-checking, o que dialoga com a ideia de edição semântica.</p>
<h3>Nível 2: Acertam Várias Coisas<span class="hx:absolute hx:-mt-20" id="nível-2-acertam-várias-coisas"></span>
    <a href="#n%c3%advel-2-acertam-v%c3%a1rias-coisas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Rust<span class="hx:absolute hx:-mt-20" id="rust"></span>
    <a href="#rust" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Não pelos motivos que as pessoas normalmente citam. O borrow checker é essencialmente um <em>sistema de efeitos verificado pelo compilador</em> para memória — rastreia aliasing, mutação e lifetime no nível de tipos. O sistema de traits com <code>Send</code>, <code>Sync</code>, <code>Unpin</code> é rastreamento de efeitos por outro nome. A convenção <code>Result&lt;T, E&gt;</code> com <code>match</code> exaustivo dá retornos explícitos. O <code>cargo</code> fornece um grafo real de dependências que pode ser consultado.</p>
<p><strong>Faltam:</strong> especificações formais, proveniência, checagem de totalidade, e a sintaxe é complexa o suficiente para LLMs gastarem tokens com ginástica de lifetime.</p>
<h4>Haskell<span class="hx:absolute hx:-mt-20" id="haskell"></span>
    <a href="#haskell" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>O clássico de várias dessas ideias. Pureza por padrão significa que efeitos são <em>sempre</em> explícitos (monad <code>IO</code>). O sistema de tipos é poderoso o bastante para codificar muitos invariantes. <code>hlint</code> e typed holes dão feedback estruturado.</p>
<p><strong>Faltam:</strong> a história de efeitos é grosseira (apenas <code>IO</code> vs puro — sem granularidade sobre <em>que tipo</em> de I/O), sem proveniência, sem linguagem de especificação embutida, e o modelo de avaliação preguiçosa torna raciocinar sobre performance genuinamente difícil até para LLMs.</p>
<h4>Dafny<span class="hx:absolute hx:-mt-20" id="dafny"></span>
    <a href="#dafny" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Linguagem do Microsoft voltada a verificação. Tem <code>requires</code>, <code>ensures</code>, <code>invariant</code> como sintaxe de primeira classe — exatamente as anotações <code>:invariants</code> descritas acima. O verificador checa isso em tempo de compilação. Terminação de loops é verificada (<code>decreases</code> = totalidade). É praticamente &ldquo;programação pesada em especificação&rdquo;.</p>
<p><strong>Fraqueza:</strong> ecossistema pequeno, sem sistema de efeitos, e orientada a verificação de algoritmos mais do que construção de sistemas.</p>
<h3>Nível 3: Acertam Uma ou Duas Coisas<span class="hx:absolute hx:-mt-20" id="nível-3-acertam-uma-ou-duas-coisas"></span>
    <a href="#n%c3%advel-3-acertam-uma-ou-duas-coisas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><h4>Elixir/Erlang (BEAM)<span class="hx:absolute hx:-mt-20" id="elixirerlang-beam"></span>
    <a href="#elixirerlang-beam" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Escolha surpreendente, mas: as árvores de supervisão do OTP são essencialmente um <strong>grafo declarativo de dependência e falhas</strong>. O modelo de processos dá isolamento natural de efeitos — cada processo é um boundary. Pattern matching com tuplas tagueadas (<code>{:ok, result}</code> / <code>{:error, reason}</code>) é retorno de união explícita. <code>@spec</code> e <code>@doc</code> são metadados inline. Hot code reloading é um &ldquo;patch semântico&rdquo; primitivo.</p>
<p><strong>Faltam:</strong> verificação formal, imposição de tipos em tempo de compilação (Dialyzer é opcional e incompleto), sem proveniência.</p>
<h4>Scala 3<span class="hx:absolute hx:-mt-20" id="scala-3"></span>
    <a href="#scala-3" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>O trabalho em sistema de efeitos (Caprese/capture checking) está indo na direção certa. Union types, match types e opaque types dão especificações expressivas de retorno. Metaprogramação inline via <code>scala.quoted</code> opera em ASTs tipadas. Mas carrega um enorme pacote de complexidade JVM.</p>
<h4>Ada/SPARK<span class="hx:absolute hx:-mt-20" id="adaspark"></span>
    <a href="#adaspark" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>O subset SPARK é formalmente verificável com contratos (<code>Pre</code>, <code>Post</code>, <code>Contract_Cases</code>). Usado em aeroespacial e defesa, onde &ldquo;provar que não pode quebrar&rdquo; é exigência real. Muito próximo ao conceito de <code>:total</code> + <code>:invariants</code>. Mas a linguagem é verbosa, o ecossistema é pequeno e não há rastreamento de efeitos além do que contratos expressam.</p>
<h3>O Padrão Desconfortável<span class="hx:absolute hx:-mt-20" id="o-padrão-desconfortável"></span>
    <a href="#o-padr%c3%a3o-desconfort%c3%a1vel" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>O Que LLMs Querem</th>
          <th>Quem Tem</th>
          <th>Por Que Não É Mainstream</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Especificações formais como código</td>
          <td>Lean, F*, Dafny</td>
          <td>Curva de aprendizado íngreme para humanos</td>
      </tr>
      <tr>
          <td>Rastreamento de efeitos</td>
          <td>F*, Haskell, Rust (parcial)</td>
          <td>Aumenta a carga de anotações</td>
      </tr>
      <tr>
          <td>Checagem de totalidade</td>
          <td>Lean, Idris, Agda</td>
          <td>Rejeita muitos programas &ldquo;úteis&rdquo;</td>
      </tr>
      <tr>
          <td>Manipulação rica de AST</td>
          <td>Lean, Lisp/Racket, Elixir macros</td>
          <td>Humanos acham macros confusas</td>
      </tr>
      <tr>
          <td>Retornos exaustivos</td>
          <td>Rust, Haskell, OCaml</td>
          <td>Humanos acham <code>match</code> tedioso vs exceções</td>
      </tr>
  </tbody>
</table>
<p>Cada recurso existe em algum lugar. A razão de nenhuma linguagem combinar tudo é que <strong>cada um adiciona carga cognitiva para humanos</strong>. A história do design de linguagens mainstream é sobre <em>remover</em> as coisas que LLMs acham mais úteis, porque humanos as experimentam como fricção.</p>
<p>Essa é a tensão central: <strong>a linguagem ideal para IA é a que humanos continuam rejeitando.</strong></p>
<hr>
<h2>Adendo: Pensamentos Adicionais<span class="hx:absolute hx:-mt-20" id="adendo-pensamentos-adicionais"></span>
    <a href="#adendo-pensamentos-adicionais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Primitivos extras que parecem especialmente úteis para LLMs<span class="hx:absolute hx:-mt-20" id="primitivos-extras-que-parecem-especialmente-úteis-para-llms"></span>
    <a href="#primitivos-extras-que-parecem-especialmente-%c3%bateis-para-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Serialização canônica e sem perda da AST</strong> — Uma representação única, estável, determinística, diffável e hashável. Isso remove drift de formatter e torna caching semântico trivial.</li>
<li><strong>Artefatos com prova acoplada</strong> — O compilador emite certificados verificáveis por máquina, amarrados a specs e efeitos, permitindo confiança incremental sem rerodar o mundo.</li>
<li><strong>Efeitos com escopo de capacidade</strong> — Efeitos deveriam exigir concessões explícitas de capacidade no boundary de módulo (não apenas declarados em assinaturas). Isso dá à linguagem um modelo de permissões embutido.</li>
<li><strong>Dualidade spec/teste</strong> — Specs devem ser executáveis e testes devem ser deriváveis de specs. A fronteira entre os dois deve ser fina e programática.</li>
</ul>
<h3>Modularização para LLMs<span class="hx:absolute hx:-mt-20" id="modularização-para-llms"></span>
    <a href="#modulariza%c3%a7%c3%a3o-para-llms" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>LLMs lidam melhor com monólitos do que humanos, mas <strong>módulos ainda importam</strong> como <em>limites semânticos</em>:</p>
<ul>
<li><strong>Escopo de efeitos</strong>: capacidades são concedidas nos boundaries de módulo.</li>
<li><strong>Propriedade e invariantes</strong>: módulos definem quais invariantes de dados possuem e impõem.</li>
<li><strong>Grafos de dependência</strong>: a linguagem pode fazer cache e re-verificar unidades menores.</li>
<li><strong>Recompilação parcial</strong>: o impacto da mudança é menor quando as unidades são isoladas.</li>
</ul>
<p>Em uma linguagem nativa para IA, modularização deve ser <strong>orientada a restrições e capacidades</strong>, não a arquivos. Você pode guardar tudo em um único arquivo, mas as <em>unidades</em> ainda devem ser explícitas.</p>
<h3>Observabilidade em runtime como primitivo da linguagem<span class="hx:absolute hx:-mt-20" id="observabilidade-em-runtime-como-primitivo-da-linguagem"></span>
    <a href="#observabilidade-em-runtime-como-primitivo-da-linguagem" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma linguagem para LLMs deveria tratar <strong>rastreamento estruturado</strong> como algo de primeira classe:</p>
<ul>
<li>Cada efeito produz eventos de trace legíveis por máquina com vínculos causais.</li>
<li>Edições podem ser validadas contra <em>deltas comportamentais</em>, não apenas diffs de tipo.</li>
<li>A proveniência pode se estender ao runtime, permitindo anexar &ldquo;por quê&rdquo; ao &ldquo;o que aconteceu&rdquo;.</li>
</ul>
<h3>Enquadramento alternativo<span class="hx:absolute hx:-mt-20" id="enquadramento-alternativo"></span>
    <a href="#enquadramento-alternativo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Em vez de uma &ldquo;linguagem&rdquo;, pense: <strong>IR nativo de grafo + protocolo de edição semântica + DSL de projeção humana</strong>. Texto vira uma view, não a fonte. A interface primária é um grafo semântico com restrições checadas, capacidades seguras e metadados com prova acoplada.</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>llms</category><category>agentes-de-codigo</category></item><item><title>[Akitando] #145 - 16 Linguagens em 16 Dias: Minha Saga da Rinha de Backend</title><link>https://akitaonrails.github.io/2023/09/20/akitando-145-16-linguagens-em-16-dias-minha-saga-da-rinha-de-backend/</link><guid isPermaLink="true">https://akitaonrails.github.io/2023/09/20/akitando-145-16-linguagens-em-16-dias-minha-saga-da-rinha-de-backend/</guid><pubDate>Wed, 20 Sep 2023 15:35:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/EifK2a_5K_U"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;A Rinha de Backend que aconteceu em Agosto de 2023 foi muito divertida. Eu só fiquei sabendo quando acabou, mas não quer dizer que não pude me divertir. Hoje quero resumir tudo que eu fiz nos 16 dias seguintes do evento, detalhes sobre os projetos dos participantes, a controvérsia do Ranking de Linguagens, quais os truques por trás dos vencedores, e como você também poderia ser um vencedor!&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/EifK2a_5K_U"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>A Rinha de Backend que aconteceu em Agosto de 2023 foi muito divertida. Eu só fiquei sabendo quando acabou, mas não quer dizer que não pude me divertir. Hoje quero resumir tudo que eu fiz nos 16 dias seguintes do evento, detalhes sobre os projetos dos participantes, a controvérsia do Ranking de Linguagens, quais os truques por trás dos vencedores, e como você também poderia ser um vencedor!</p>
<p>Finalmente vou demonstrar o que significa &ldquo;ser promíscuo&rdquo; com linguagens de programação. Vamos entender porque como de fato ler um ranking. E como podemos fazer TODO MUNDO alcançar o primeiro lugar do Rust!</p>
<h2>Conteúdo<span class="hx:absolute hx:-mt-20" id="conteúdo"></span>
    <a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00:00 - Intro</li>
<li>00:01:41 - CAP 01 - As Regras - Requerimentos da Rinha</li>
<li>00:09:46 - CAP 02 - Os Participantes - Vencedores da Rinha</li>
<li>00:15:25 - CAP 03 - Dia 1: Entrando na Rinha - Minha versão em Ruby on Rails</li>
<li>00:21:08 - CAP 04 - MrPowerGamerBR entra em cena - A chegada dos Piratas!</li>
<li>00:27:55 - CAP 05 - Tentando com Crystal - Aprendendo Lucky Framework</li>
<li>00:34:01 - CAP 06 - Dia 8: Gerenciando Baratie - Fluxo de Restaurante</li>
<li>00:38:37 - CAP 07 - Consertando Meu Rails - Aprendendo com Erros</li>
<li>00:42:54 - CAP 08 - Aprendendo com Node.js - Muita Refatoração</li>
<li>00:49:48 - CAP 09 - Apanhando de Erlang - Segredos de Elixir</li>
<li>00:55:51 - CAP 10 - Explorando Go Lang - Essa foi fácil</li>
<li>00:57:31 - CAP 11 - Dando Moral pra NATS - C# Vencedor</li>
<li>00:58:15 - CAP 12 - Dia 12: Explorando PHP Moderno - De Node a Swoole</li>
<li>01:00:12 - CAP 13 - Elevando Python - Pequeno Erro</li>
<li>01:02:21 - CAP 14 - &ldquo;foi lá, E FEZ&rdquo; - A Saga de Lean4</li>
<li>01:06:28 - CAP 15 - Pré-Feriado: Tentando NIM - A decepção</li>
<li>01:08:47 - CAP 16 - Diário de Bordo: Resumo - Chegando na Grand Line</li>
<li>01:13:05 - CAP 17 - Feriadão! - Encontrando o One Piece??</li>
<li>01:19:16 - CAP 18 - Testando o One Piece! Todos ao Primeiro Lugar!</li>
<li>01:27:16 - CAP 19 - Conclusão: Por que todo mundo não usa Rust?? - Escala de Mercados</li>
<li>01:37:33 - Bloopers</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Repo Oficial da Rinha: <a href="https://github.com/zanfranceschi/rinha-de-backend-2023-q3/tree/main"target="_blank" rel="noopener">https://github.com/zanfranceschi/rinha-de-backend-2023-q3/tree/main</a></li>
<li>MrPowerGamerBR: # Os Resultados da RINHA DE BACKEND estão ERRADOS, e eu posso provar <a href="https://www.youtube.com/watch?v=XqYdhlkRlus"target="_blank" rel="noopener">https://www.youtube.com/watch?v=XqYdhlkRlus</a></li>
<li>Raciocínio Automatizado com Leonardo de Moura, Pesquisador na Microsoft Research <a href="https://dev.to/elixir_utfpr/raciocinio-automatizado-com-leonardo-de-moura-pesquisador-na-microsoft-research-4d1k"target="_blank" rel="noopener">https://dev.to/elixir_utfpr/raciocinio-automatizado-com-leonardo-de-moura-pesquisador-na-microsoft-research-4d1k</a></li>
<li>Rinha versão Algebraic <a href="https://github.com/meoowers/rinha"target="_blank" rel="noopener">https://github.com/meoowers/rinha</a>
Repositórios da Sofia <a href="https://github.com/algebraic-sofia?tab=repositories"target="_blank" rel="noopener">https://github.com/algebraic-sofia?tab=repositories</a></li>
</ul>
<h2>Tweets<span class="hx:absolute hx:-mt-20" id="tweets"></span>
    <a href="#tweets" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Tweet: Início da minha versão de Rails <a href="https://x.com/AkitaOnRails/status/1695526606503022923?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1695526606503022923?s=20</a></li>
<li>Tweet: O ranking oficial não é importante, discutir as técnicas, sim <a href="https://twitter.com/AkitaOnRails/status/1695913459202838839"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1695913459202838839</a></li>
<li>Tweet: 1o resultado com Crystal/Lucky <a href="https://x.com/AkitaOnRails/status/1696207752043872433?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1696207752043872433?s=20</a></li>
<li>Tweet: Minhas impressões aprendendo framework Lucky, em Crystal <a href="https://twitter.com/AkitaOnRails/status/1696220345152176478"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1696220345152176478</a></li>
<li>Tweet Leandro: diminuir workers do nginx melhora uso no lado do postgres <a href="https://x.com/leandronsp/status/1696292520106299417?s=20"target="_blank" rel="noopener">https://x.com/leandronsp/status/1696292520106299417?s=20</a></li>
<li>Tweet: Mais discussão das técnicas de otimização como batch insert <a href="https://x.com/AkitaOnRails/status/1696255958328938745?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1696255958328938745?s=20</a></li>
<li>Tweet Leandro: experimentando a teoria de diminuir workers e pool na versão de Ruby, conseguindo quase 30k <a href="https://x.com/leandronsp/status/1696219396316836331?s=20"target="_blank" rel="noopener">https://x.com/leandronsp/status/1696219396316836331?s=20</a></li>
<li>Tweet: contribuindo com o projeto Kiwi e comentando sobre LevelDB <a href="https://x.com/AkitaOnRails/status/1697283886156226818?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1697283886156226818?s=20</a></li>
<li>Tweet: meu mantra da programação <a href="https://x.com/AkitaOnRails/status/1697317688006066432?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1697317688006066432?s=20</a></li>
<li>Tweet: 1o resultado positivo com Crystal <a href="https://x.com/AkitaOnRails/status/1697651647273427429?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1697651647273427429?s=20</a></li>
<li>Tweet: 1o resultado positivo com Rails <a href="https://x.com/AkitaOnRails/status/1698475713920119117?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698475713920119117?s=20</a></li>
<li>Tweet: mexendo na versão Node do Lucas Weis <a href="https://x.com/AkitaOnRails/status/1698565631560282590?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698565631560282590?s=20</a></li>
<li>Tweet: desafio: todas conseguem bater os 40k <a href="https://x.com/AkitaOnRails/status/1698491304366219744?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698491304366219744?s=20</a></li>
<li>Tweet: apanhando da versão Elixir do Gabriel Oliveira <a href="https://x.com/AkitaOnRails/status/1698760144924905474?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698760144924905474?s=20</a></li>
<li>Tweet: revisando a versão Elixir <a href="https://x.com/AkitaOnRails/status/1698809091055874491?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698809091055874491?s=20</a></li>
<li>Tweet Gabriel: consertando o crash de Elixir no meu PC <a href="https://x.com/oliveigah/status/1698866344853151857?s=20"target="_blank" rel="noopener">https://x.com/oliveigah/status/1698866344853151857?s=20</a></li>
<li>Tweet: resolvido problema do Elixir <a href="https://twitter.com/AkitaOnRails/status/1698888695376437677"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698888695376437677</a></li>
<li>Tweet: mexendo no Go do Luan, 29k <a href="https://x.com/AkitaOnRails/status/1698818634397647092?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1698818634397647092?s=20</a></li>
<li>Tweet: fuçando a versão finalista de Dotnet <a href="https://twitter.com/AkitaOnRails/status/1698851765658013900"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698851765658013900</a></li>
<li>Tweet: 1o resultado da versão PHP do Lauro <a href="https://twitter.com/AkitaOnRails/status/1698923556631990453"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698923556631990453</a></li>
<li>Tweet: 1o resultado da versão Python do Ian Cambrea: <a href="https://twitter.com/AkitaOnRails/status/1698966472083517931"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698966472083517931</a></li>
<li>Tweet: Primeiro contato com Lean4 <a href="https://twitter.com/AkitaOnRails/status/1699064101111218212"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1699064101111218212</a></li>
<li>Tweet: Bibliotecas em Lean da Sofia <a href="https://twitter.com/AkitaOnRails/status/1699069812079767739"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1699069812079767739</a></li>
<li>Tweet: Resumo até agora <a href="https://twitter.com/AkitaOnRails/status/1698900197068153027"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698900197068153027</a></li>
<li>Tweet: Resumo das técnicas até agora <a href="https://twitter.com/AkitaOnRails/status/1698903125338226739"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698903125338226739</a></li>
<li>Tweet: Tentando versão em Nim (fracasso) <a href="https://twitter.com/AkitaOnRails/status/1699533869739856257"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1699533869739856257</a></li>
<li>Tweet: versão nova em Java do Junior Leão <a href="https://twitter.com/juniorleaoo/status/1699427149831114850"target="_blank" rel="noopener">https://twitter.com/juniorleaoo/status/1699427149831114850</a></li>
<li>Tweet: versão desclassificada em V Lang do Carlos Silva <a href="https://twitter.com/_insalubre/status/1699498542245544269"target="_blank" rel="noopener">https://twitter.com/_insalubre/status/1699498542245544269</a></li>
<li>Tweet: Leandro explica a lógica de diminuir vazão do nginx <a href="https://twitter.com/leandronsp/status/1699568664859603184"target="_blank" rel="noopener">https://twitter.com/leandronsp/status/1699568664859603184</a></li>
<li>Tweet: resumo das técnicas até agora: <a href="https://twitter.com/AkitaOnRails/status/1698903125338226739"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1698903125338226739</a></li>
<li>Tweet: Vinicius Ferraz descobre e publica network_mode: host <a href="https://twitter.com/AkitaOnRails/status/1700488994323128673"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1700488994323128673</a></li>
<li>Tweet: Reinaldo/Zsantana, atinge Zero knockouts: <a href="https://twitter.com/reijsantana/status/1701000310280573287"target="_blank" rel="noopener">https://twitter.com/reijsantana/status/1701000310280573287</a></li>
<li>Tweet: Chegando de viagem e ajustando todo mundo de novo: <a href="https://x.com/AkitaOnRails/status/1701230737922339143?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701230737922339143?s=20</a></li>
<li>Tweet: Rails bate 45k <a href="https://x.com/AkitaOnRails/status/1701248237586219416?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701248237586219416?s=20</a></li>
<li>Tweet: Ruby do Leandro bate 47k <a href="https://x.com/AkitaOnRails/status/1701423430870954312?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701423430870954312?s=20</a></li>
<li>Tweet: Rails do Lazaro bate 46k <a href="https://x.com/AkitaOnRails/status/1701446727910199459?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701446727910199459?s=20</a></li>
<li>Tweet: Node.js bate 46k <a href="https://x.com/AkitaOnRails/status/1701295880844775425?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701295880844775425?s=20</a></li>
<li>Tweet: Go do Luan bate 46k <a href="https://x.com/AkitaOnRails/status/1701286259459523033?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701286259459523033?s=20</a></li>
<li>Tweet: Bun bate 43k <a href="https://x.com/AkitaOnRails/status/1701630742642471420?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701630742642471420?s=20</a></li>
<li>Tweet: Python bate 46k <a href="https://x.com/AkitaOnRails/status/1701267343521767606?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701267343521767606?s=20</a></li>
<li>Tweet: V Lang bate 46k <a href="https://x.com/AkitaOnRails/status/1701256544904438210?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701256544904438210?s=20</a></li>
<li>Tweet: Consertando Java do Reinaldo, bate 46k <a href="https://x.com/AkitaOnRails/status/1701402351913910693?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1701402351913910693?s=20</a></li>
<li>Tweet: C++ do Lucas William bate 46k <a href="https://x.com/AkitaOnRails/status/1702008261736534294?s=20"target="_blank" rel="noopener">https://x.com/AkitaOnRails/status/1702008261736534294?s=20</a></li>
<li>Tweet: Refatorando o Rust do Vinicius <a href="https://twitter.com/AkitaOnRails/status/1701816747274260698"target="_blank" rel="noopener">https://twitter.com/AkitaOnRails/status/1701816747274260698</a></li>
</ul>
<h2>Pull Requests<span class="hx:absolute hx:-mt-20" id="pull-requests"></span>
    <a href="#pull-requests" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>PR versão Node.js <a href="https://github.com/lukas8219/rinha-be-2023-q3/pull/1"target="_blank" rel="noopener">https://github.com/lukas8219/rinha-be-2023-q3/pull/1</a></li>
<li>PR versão Node.js, network mode <a href="https://github.com/lukas8219/rinha-be-2023-q3/pull/2"target="_blank" rel="noopener">https://github.com/lukas8219/rinha-be-2023-q3/pull/2</a></li>
<li>PR versão Go Lang <a href="https://github.com/luanpontes100/rinha-de-backend-2023-q3-golang/pull/1"target="_blank" rel="noopener">https://github.com/luanpontes100/rinha-de-backend-2023-q3-golang/pull/1</a></li>
<li>PR versão Go Lang, network mode <a href="https://github.com/luanpontes100/rinha-de-backend-2023-q3-golang/pull/1"target="_blank" rel="noopener">https://github.com/luanpontes100/rinha-de-backend-2023-q3-golang/pull/1</a></li>
<li>PR versão PHP/Swoole <a href="https://github.com/lauroappelt/rinha-de-backend-2023/pull/1"target="_blank" rel="noopener">https://github.com/lauroappelt/rinha-de-backend-2023/pull/1</a></li>
<li>PR versão Python/Sanic <a href="https://github.com/iancambrea/rinha-python-sanic/pull/1"target="_blank" rel="noopener">https://github.com/iancambrea/rinha-python-sanic/pull/1</a></li>
<li>PR versão de Lean4 fix de Location: <a href="https://github.com/meoowers/rinha/pull/1"target="_blank" rel="noopener">https://github.com/meoowers/rinha/pull/1</a></li>
<li>PR versão de Lean4 fix de data: <a href="https://github.com/meoowers/rinha/pull/2"target="_blank" rel="noopener">https://github.com/meoowers/rinha/pull/2</a></li>
<li>Versão em Nim/Jesper: <a href="https://github.com/akitaonrails/rinhabackend-nim-jester-api"target="_blank" rel="noopener">https://github.com/akitaonrails/rinhabackend-nim-jester-api</a></li>
<li>Versão em Java/Quarkus: <a href="https://github.com/zsantana/rinha-backend-by-bruno-borges/tree/main"target="_blank" rel="noopener">https://github.com/zsantana/rinha-backend-by-bruno-borges/tree/main</a></li>
<li>PR versão de C++: <a href="https://github.com/lucaswilliameufrasio/backend-cockfighting-q3-2023/pull/1"target="_blank" rel="noopener">https://github.com/lucaswilliameufrasio/backend-cockfighting-q3-2023/pull/1</a></li>
<li>PR versão de C# do Zan: <a href="https://github.com/zanfranceschi/rinha-de-backend-2023-q3-csharp/pull/3"target="_blank" rel="noopener">https://github.com/zanfranceschi/rinha-de-backend-2023-q3-csharp/pull/3</a></li>
<li>PR versão de Ruby do Leandro: <a href="https://github.com/leandronsp/rinha-backend-ruby/pull/4"target="_blank" rel="noopener">https://github.com/leandronsp/rinha-backend-ruby/pull/4</a></li>
<li>PR versão de Bun: <a href="https://github.com/Met4tron/rinha-bun/pull/1"target="_blank" rel="noopener">https://github.com/Met4tron/rinha-bun/pull/1</a></li>
<li>PR versão de Java do Reinaldo, fix de data: <a href="https://github.com/zsantana/rinha-backend-sem-cache/pull/1"target="_blank" rel="noopener">https://github.com/zsantana/rinha-backend-sem-cache/pull/1</a></li>
<li>PR versão de V Lang, network mode <a href="https://github.com/carlosqsilva/rinha-2023-q3/pull/1"target="_blank" rel="noopener">https://github.com/carlosqsilva/rinha-2023-q3/pull/1</a></li>
<li>PR versão de Rust do Vinicius, refactoring e fix: <a href="https://github.com/viniciusfonseca/rinha-backend-rust/pull/2"target="_blank" rel="noopener">https://github.com/viniciusfonseca/rinha-backend-rust/pull/2</a></li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Se costuma acompanhar a comunidade de desenvolvimento no Twitter, talvez tenha ouvido falar que em agosto de 2023 aconteceu um mini evento online chamado de Rinha de Backend, organizado pelo Zanfranceschi, desenvolvedor experiente, atualmente trabalhando no Nubank.</p>
<p>Eu não conhecia ele e só fiquei sabendo que esse evento aconteceu quase uma semana depois que já tinha acabado. Mesmo se soubesse antes não teria participado porque nunca fui de competições, hackathons ou algo assim. Mas eu gostei muito do que os participantes fizeram e, sem querer, isso tomou boa parte do meu tempo, incluindo várias noites mau dormidas.</p>
<p>Sem sombra de dúvidas este vai ser o video mais trabalhoso que já fiz na história deste canal. Não só este como o próximo. Hoje vou resumir minha saga de 16 dias do pós-rinha avaliando diversos projetos dos participantes. Neste percurso, aprendi muita coisa nova que não sabia ou que achava que fosse ser diferente. Foi uma longa saga, muitas noites mau dormidas, frustração, ansiedade, crises existenciais, suor e lágrimas. É bastante coisa e só o resumo vai dar um video de mais de 1 hora.</p>
<p>Por isso vou pedir muita paciência de vocês. Vai ter várias técnicas importantes que vou mencionar o nome e os efeitos que causam, mas vai ser bem por cima. Se ficarem confusos com termos que parecem complicados, não se preocupem, vou explicar os detalhes no próximo. Também vai ser no próximo que vou falar detalhes das linguagens que vou mencionar. Então segurem as críticas. Hoje quero só compartilhar como foi minha experiência.</p>
<p>Primeiro vou resumir como foi o evento em si, um pouco sobre os projetos que ganharam e o ranking que gerou alguma controvérsia, pelo que ouvi falar. Daí vou contar o que pensei a respeito e como resolvi começar a abrir os projetos dos participantes com uma missão: fazer todo mundo chegar no primeiro lugar!</p>
<p>(&hellip;)</p>
<p>Como disse, eu não participei do evento, nem acompanhei o dia a dia. Acho que o desafio foi lançado primeiro mas ninguém sabia exatamente como seriam os critérios pra definir o vencedor até dias depois. E parte da graça de uma &ldquo;rinha&rdquo; como essa não é ser uma competição tecnicamente, perfeitamente justa, tem muito fator sorte e incerteza envolvido, como em qualquer esporte.</p>
<p>Nem sempre é o melhor atleta técnico que ganha todos os jogos, mas quem aprendeu mais nos jogos anteriores, quem consegue se adaptar rápido e quem teve menos azar. Assim foi esta rinha também, por isso em nenhum momento encarem qualquer coisa que parece uma crítica como uma crítica ao evento e sim como dicas pros próximos. Minha discussão vai ser puramente técnica. A crítica real vai pra quem tentou levar isso a sério demais. Ser torcedor apaixonado de um time é normal, sair xingando e brigando na rua quando vê o cara do outro time é ser um animal irracional imbecil. Vou assumir que estou falando com homo sapiens e não neandertais.</p>
<p>O desafio foi divulgado num repositório de GitHub do Zan que vou deixar o link nas descrições abaixo. Aliás, sempre olhem a descrição de todo video. Eu coloco links pra várias coisas extras pra vocês. Além de ter a descrição de todos os capítulos pra ficar mais fácil achar seções de videos longos como este. Enfim, leiam as instruções do desafio mas em resumo, o objetivo era escolher qualquer linguagem de programação, com ou sem frameworks, implementar 4 endpoints de APIs e configurar pra rodar com Docker Compose numa configuração específica de infraestrutura.</p>
<p>Fazer a API em si, em qualquer linguagem, é super simples. É um endpoint POST &ldquo;/pessoas&rdquo; que recebe um JSON simples com dados como apelido, nome, data de nascimento e stack de programação, um array com elementos como &ldquo;php&rdquo; ou &ldquo;java&rdquo;. É como se fosse o cadastro de um sisteminha de recrutamento ou algo assim. Tem que validar que apelido tem no máximo 32 letras, nome no máximo 100 letras, data de nascimento tem que ser minimamente válido. Inclusive ele não fala que 31 de fevereiro tem que ser inválido, só fala que o formato tem que ser ano, tracinho, me, tracinho, dia.</p>
<p>A lista de stack tem que ser um array sem elementos nulos e cada elemento não podia ter mais que 32 letras. E finalmente, o ID tinha que ser um UUID, que é um tipo de número aleatório. Isso foi um bom requerimento como vou mostrar mais pra frente. Mas em termos de validação era só isso. Se passar dados inválidos temos que devolver o cabeçalho HTTP 400 de bad request.</p>
<p>Ou se voltar erro no insert do SQL por causa de constraints inválidas como nome não poder ser nulo, apelido já existir ou algo assim, tem que devolver cabeçalho HTTP 422 de erro unprocessable entity. A resposta pra requisição de criação precisa ter o cabeçalho HTTP 201 created e um cabeçalho &ldquo;Location&rdquo; com o caminho pra essa pessoa recém criada no formato GET &ldquo;/pessoa/uuid&rdquo;. Isso é comum numa API restful básica.</p>
<p>Esse endpoint devolvido no Location deve conseguir encontrar a pessoa recém criada e devolver o JSON com os dados. Se passar um UUID que não existe, tem que devolver o cabeçalho HTTP de 404 not found. Depois precisava ter outro endpoint GET &ldquo;/pessoas&rdquo; que receba um parâmetro chamado &ldquo;t&rdquo; pra ser um &ldquo;termo de procura&rdquo;, pra pesquisarmos o termo no apelido ou nome ou na stack. Seria tipo fazer um SQL com apelido like &ldquo;%termo%&rdquo; OR nome like termo OR stack like termo. Já vou explicar porque esse seria o pior jeito.</p>
<p>Devolvemos o resultado em JSON com código HTTP 200 ou devolve código 400 de erro se não passar termo nenhum. Não encontrando nada bastava devolver um JSON com array vazio em vez de 404 not found. E finalmente o 4o endpoint era um simples GET &ldquo;contagem-erro&rdquo; que deveria fazer um count na tabela e devolver quantas inserções de fato aconteceram. Esse é o único endpoint que não faz parte do stress test e isso pode se tornar um ponto importante pra alguns truques como vou explicar no próximo video.</p>
<p>Como podem ver, são endpoints super triviais. Se você é um programador experiente tenho certeza que já tem de cabeça como faria na sua linguagem mais fluente. Dá pra fazer rapidinho uma versão que funciona em 1 ou 2 horas se estiver um pouco enferrujado. Mesmo se for bem iniciante, seguindo qualquer tutorial de APIs restful no framework que estiver aprendendo, é pra conseguir fazer em 1 dia. Essa é a ordem de grandeza e talvez sirva pra vocês saberem em que pé estão. Vai demorar mais do que isso se quiser experimentar técnicas diferentes. Teve versão que eu levei uns dois dias pra terminar porque resolvei aprender um framework novo do zero, por exemplo.</p>
<p>No próximo video vou demonstrar como dá pra fazer uma versão simples em poucos minutos, por agora só acreditem que dá. Aliás, vamos começar a listar coisas pro próximo episódio. &ldquo;Mostrar como fazer uma versão simples do zero&rdquo;. Mas agora vem a parte que separa os adultos das crianças: a infraestrutura. Não basta só fazer o código e dizer &ldquo;roda na minha máquina&rdquo;. Tem que rodar dentro das restrições da rinha. Então não é só subir um banco de dados, subir sua aplicação sozinha e acabou.</p>
<p>Segundo as regras, tem que subir duas instâncias da sua aplicação, pra rodar em paralelo. E ambos precisam ficar embaixo de um balanceador de carga, no caso pediram o Nginx. No mundo real aplicações rodam com várias instâncias em paralelo embaixo de um balanceador de um Kubernetes, Seasaw, HAProxy ou várias outras alternativas. Azure tem o Application Gateway, AWS tem o ELB, o Elastic Load Balancing, tem o Traefik, F5 Big-IP e mais. Mas NGINX é fácil e simples o suficiente pra este desafio. Não é o mais rápido, mas também não é o mais difícil de usar.</p>
<p>E aqui começa a parte realmente difícil: uma instância de NGINX, uma instância de banco de dados, que pode ser postgres, mysql ou mongodb, mais duas instâncias da sua aplicação sendo balanceadas, precisam caber em 1 CPU e meio e no máximo usar 3 gigabytes de RAM e só. Essa é a regra da rinha. Fazer a API não é o desafio. Essa limitação que é. Mas tudo bem, fazer caber é fácil, o problema mesmo é que no final esse sistema tem que aguentar um stress test, um teste de carga.</p>
<p>Stress test é um teste automatizado que simula como pessoas ou sistemas consumiriam o seu sistema, mandando requisições http pros endpoints da API. No caso eles escolheram a ferramenta Gatling, que tem versão aberta e uma paga com mais recursos. A versão gratuita é razoavelmente simples de usar e o script pro cenário de carga está no GitHub da rinha. Vamos anotar aqui pra eu explicar &ldquo;O Básico de Stress Test&rdquo; no próximo video.</p>
<p>Lembrando que quando a rinha começou acho que os participantes ainda não tinham acesso a esse script de testes, justamente pra não tentar burlar os critérios. Eu não sei se eles sequer tiveram acesso ao script durante a rinha. Imagino que não, só no final. Vamos assumir que ninguém sabia. Isso que diferencia os participantes da rinha de programadores como eu agora, que tem a oportunidade de avaliar sabendo tudo que eles não sabiam. Não comparem o meu resultado com o deles, isso seria injusto.</p>
<p>Em resumo, você podia escolher linguagem, framework e implementar uma aplicação web que serve 4 endpoints de APIs. Tinha que ser o mais leve possível pra aguentar carregar duas instâncias, mais nginx, mais banco, em 1.5 CPUs e 3GB de RAM, orquestrado por docker compose.</p>
<p>Eu ainda não fiz um video explicando o que é um framework web, então vamos anotar aqui pra explicar no próximo video. Eu já tinha feito um video sobre Docker, recomendo que assistam depois, mas aí também no próximo vou explicar como usar o Docker Composer pra subir todos os componentes de um sistema web na sua máquina também.</p>
<p>A idéia é que os participantes deviam empacotar a aplicação numa imagem de Docker, subir no Dockerhub, postar o arquivo de docker compose como pull request no GitHub da rinha e aí os organizadores iam rodar esse docker compose e aplicar algum teste de carga. Quem passasse, ia pro ranking final.</p>
<p>O ranking foi interessante. Teve 90 participantes. Desses, 39 foram desclassificados por diversas razões. Ou o docker compose tinha algum bug e nem carregava, ou algum dos endpoints da API não respondia como deveria, ou deixaram de implementar alguma coisa que tinha nas regras. Sobraram 51 projetos que funcionaram, mais da metade. Um número respeitável. No final, além de implementar os endpoints corretamente, o resultado pro ranking foi baseado na quantidade de inserts que foram feitos no banco de dados.</p>
<p>Ordenando a lista por essa quantidade de inserts, tivemos os Top 10. Quem ganhou a rinha foi o Vinicius Fonseca, com sua implementação usando Rust, com o framework Actix com Tokio. Em segundo lugar ficou o Leo Vargas com sua versão em Go e Fiber, o framework que usa FastHTTP. Em terceiro ficou o André Marra e Albert Kliemke, que fizeram em dupla uma versão em Dotnet 8 com C#. Em quarto ficou a Isadora Souza, que também usou Go com Gin. Em quinto ficou a dupla algebraic, a Sofia e a Gabi, que fizeram em Lean4, a linguagem mais desconhecida da rinha toda. Depois vou falar delas.</p>
<p>Em sexto ficou o Vinicius Santos que também fez em Rust. Em sétimo ficou o Jean Rodrigues que também fez em Go. Em oitavo ficou o Luiz Picanço, com outra versão em Rust. Em nono lugar &ldquo;e&rdquo; também em décimo ficou o Rodrigo Navarro, que participou com duas versões de Rust, uma feita com o framework Axum e outra com o mais leve Touche que, por ironia, ficou depois da versão em Axum. Aliás, parece que ele fez pelo menos uma dessas versões numa live no canal dele, pra quem se interessar corre atrás pra assistir.</p>
<p>Portanto, nos Top 10 ficou assim: 5 versões em Rust, 3 versões em Go, 1 em Lean e 1 em dotnet. Aí as redes sociais foram à loucura. Primeiro, ninguém achou estranho já que isso confirma a hegemonia do Rust e do Go como as linguagens mais rápidas da atualidade. Pior ainda: o fato de dar um dotnet nos Top 10 mas não um Java, fez um monte de gente ficar ridicularizando o Java. Sabe o que falei do torcedor de time neandertal? Sim, afinal na cabeça deles, Java é uma linguagem velha e ultrapassada, certo? Vamos ver como os neandertais leram o resto do ranking.</p>
<p>Seguindo pros próximos 10. Em décimo primeiro ficou o Gabriel Oliveira com uma versão em Elixir. Alguns ficaram tipo, &ldquo;puts, não era o Elixir que se vendia como rápido? Perdeu até pro dotnet?&rdquo; Em décimo segundo ficou o Lucas William com uma versão em C++. &ldquo;Ué, C++ não era pra ser o mais rápido? Rust e Go são foda mesmo, muito melhor que C++&rdquo;. Em décimo terceiro ficou o Lucas Weis com uma versão em Node.js com Express. &ldquo;Pois é, bem que me falaram que Javascript não é tudo isso que falam, né?&rdquo;</p>
<p>Em décimo quarto ficou o Yuri Gomes que fez uma versão em Bun, antes do lançamento da versão 1.0 alguns dias atrás. E pois é, Bun é só hype mesmo, nem lançou ainda e já é lento. Em décimo quinto ficou o Thales Maciel que fez em Rust também. Coitado, deve ser um júnior que né, não é culpa do Rust. Em décimo sexto ficou um cara de apelido saiintbrisson, que não deixou arquivo de README pra trás com o nome nem links, então nem fui tentar procurar.</p>
<p>Em décimo sétimo ficou o Lauro Appelt com sua versão em PHP. Finalmente uma linguagem que se associa mais com Web clássica. Quem diria, PHP ainda não morreu, mas tá lá embaixo com um pé na cova né? E só em décimo oitavo ficou o Bruno Borges com a primeira versão em Java da rinha. E só pra piorar a situação do Java, ele ficou encostado no décimo novo que foi o Lázaro Nixon com uma versão em Ruby on Rails e o vigésimo do Leandro Proença que também fez uma versão em Ruby. Peraí, Java tá tão ruim agora que fica encostado em Ruby?? Que fase heim?</p>
<p>Eu queria parar no vigésimo, mas só no vigésimo primeiro lugar tivemos o Ian Cambrea com a primeira versão em Python, usando Sanic, na competição. Pelo visto Python também é só hype né? Não ganhou nem de PHP, nem de Ruby e nem de Java. Espero que vocês que acompanharam não tenham pensado desse jeito, porque seria vergonhoso.</p>
<p>Dentre os 51 projetos enviados, 8 foram feitos em Go, 7 em javascript, 6 em Rust, 6 em Java. 5 em PHP. 4 em dotnet. 3 em Python, 2 em Ruby, 2 em Elixir, 1 em C++, 1 em Lean, e o último lugar foi 1 feito em Bash pelo Leandro. Teve uns 3 no ranking que o README não dizia em que linguagem foi feito e nem o link pro Github então também não fui atrás pra saber.</p>
<p>O Lean da Sofia e Gabi foi o mais inesperado, ainda mais por ter entrado nos Top 10 e o último de Bash do Leandro foi o mais inusitado porque acho nem ele esperaria ganhar com isso, mas quis representar e mandou super bem. Sim, Bash de terminal que vocês usam no Linux como shell. Tem gente que roda Doom numa calculadora, porque não uma aplicação web em Bash? Mas nesse caso, como esperado, Bash não tinha como competir mesmo.</p>
<p>Alguns participaram com mais de uma implementação, como o Leandro que fez em Ruby e Bash, o Lauro Appelt com duas versões em PHP, o Rodrigo Navarro com duas em Rust. Não sei como foi a divulgação do Zanfranchesci, mas ele mandou muito bem. Foi uma participação bem variada e ao meu ver cumpriu exatamente o que eu imaginaria de uma rinha: galos de diferentes tamanhos e cores. Rust e Go na frente, Javascript e Java pra trás. É o que a platéia gostaria de ver. Foi show de bola.</p>
<p>Tendo visto os resultados, se no topo tivesse sido uma hegemonia absoluta de Rust e Go provavelmente eu teria aceitado como todo mundo: sim, talvez nessas restrições de infra, só eles mesmo pra aguentar a carga. Mas como tinha um dotnet ali na terceira posição, e meus parabéns pro André e Albert por isso, mas isso chamou minha atenção: não vejo porque dotnet consegue estar nos Top 10 e outra linguagem não, seja Java ou seja Javascript.</p>
<p>Com essa pulga atrás da orelha, minha saga começa na sexta feira, dia 25 de agosto, eu abri o editor e comecei a fazer minha versão. Tava um pouco enferrujado, então levei acho que umas 2 horas pra implementar em Ruby on Rails mesmo. Levou mais tempo do que deveria porque pensei: &ldquo;hum, se é um teste de carga em inserção, talvez o postgres seja o gargalo. Depois do HTTP POST, o teste vai checar se inseriu pelo ID. Não tem como deixar só em memória porque são duas instâncias isoladas. Como tem balanceador de carga, o normal vai ser a inserção cair numa instância e a pesquisa cair na outra instância&rdquo;.</p>
<p>A conclusão imediata que qualquer um chegaria é: vou colocar um cache compartilhado, talvez memcached. Mas como queria já lidar com o que achei que fosse o gargalo, pensei, &ldquo;vou jogar jobs em fila de redis pra inserir em background&rdquo;. Então esquece memcache, vou usar redis tanto como cache quanto fila de jobs. Deve caber nas restrições de RAM porque aí o postgres deve trabalhar um pouco menos e eu divido os recursos.</p>
<p>Pelo que avaliei depois, não fui só eu que pensei assim. Várias das versões fizeram a mesma coisa: usaram redis ou nats como cache e fila de jobs assíncronos. É uma coisa que todos nós, que já temos cicatrizes de guerra, fazemos meio sem pensar e isso é um sério problema. Não existem soluções automáticas. Cada caso é um caso e precisa ser medido antes de implementar qualquer truque. Cache ou filas não deixam nada rápido automaticamente em todo caso. Eles também custam recursos. De novo, vamos deixar anotado aqui pra eu explicar no próximo video, como é essa estratégia de usar jobs e filas pra inserir e porque cache não fazia diferença aqui.</p>
<p>Pelo mesmo motivo achei que fosse ver mais gente usando MongoDB, assumindo que muitos imaginam que é mais rápido, mas do total de 90 projetos só 7 tentaram. E mesmo MySQL só 2 tentaram. A maioria esmagadora acreditou no Postgres por padrão. A conclusão: coisas relativamente fáceis, como adicionar caches e filas, todo mundo faz na frente. Coisas mais complicadas, como mudar o paradigma de armazenamento, aí deixamos pra depois. Eu também escolhei ir de Postgres.</p>
<p>Sendo eu criador de conteúdo que, por 20 anos venho evangelizando coisas como uso de Postgres, cache e jobs assíncronos com Redis, me deu uma ponta de dor na consciência. Mesmo aqui no canal já fiz um video chamado &ldquo;Tornando sua App Web Mais Rápida&rdquo;, onde faço justamente essas recomendações. Eu tento explicar em que casos usar, mas acho que dá pra explicar isso em mais detalhes, por isso deixei anotado pra voltar nesse assunto no próximo video.</p>
<p>Quando terminei minha versão de Rails, com cache, jobs e tudo mais, rodei o stress test. Já imaginava que poderia não ser rápido, mas sempre tem aquela esperança de &ldquo;vai que&rdquo; né? E pra minha decepção não deu mais que uns 15 mil inserts, abaixo ainda das versões de Rails do Lazaro e do Leandro. Foi quando comecei a postar tudo que ia descobrindo no Twitter. Esse foi meu primeiro tweet.</p>
<p>Olha que situação ridícula. Quem gera esses relatórios é a própria ferramenta Gatling, que roda o teste de carga. No próximo video vou mostrar como ele funciona. Nesse gráfico interessa a barrinha verde, olha como tá lá embaixo e a vermelha tá lá em cima. É a quantidade de knockouts ou KOs, de requisições que não conseguiram ser processadas e foram perdidas. Em resumo, só entenda que queremos que verde seja grande e vermelho seja pequeno ou idealmente, zero. Nesse momento eu pensei: &ldquo;é, talvez essa carga seja muito pesada pra uma linguagem interpretada como Ruby numa infra de docker tão apertada&rdquo;.</p>
<p>Resolvi baixar a versão do Leandro que usa Ruby mas sem framework pesado como o Rails. E os resultados dele foram bem melhores que os meus, mas ainda assim tem uma barra vermelha considerável de knockouts. Eu conseguia 15 mil inserts e a versão dele batia acima de 30 mil inserts, na minha máquina. Na minha cabeça essa discrepância fazia sentido. Imagino que façam na de vocês também, afinal quanto menos coisa tiver, mais rápido deveria ficar.</p>
<p>Só pra testar os limites, tirei a restrição de 1.5 CPU e 3GB de RAM e configurei o docker compose pra usar 24 CPUs e 20GB de RAM, afinal meu PC aguenta muito mais que isso. Assim a barrinha verde de fato sobe, mas olha que ainda tem uma barrinha vermelha. Eu ainda estou perdendo requisições, e pára pra pensar, isso não faz sentido, em nenhuma linguagem.</p>
<p>Nesse ponto as coisas não estavam fazendo muito sentido. Bacana, eu sei que Rust e Go são rápidos, afinal são compilados e tals, mas se o problema fosse só falta de recurso, na minha máquina, que é absurdamente potente, não deveria estar dando nenhum knockout. Isso foi aumentando a pulga atrás da minha orelha. A conclusão nunca pode ser &ldquo;a linguagem é uma bosta&rdquo;, a primeira conclusão sempre tem que ser &ldquo;eu estou fazendo alguma coisa errada&rdquo; e ir atrás de descobrir o que.</p>
<p>Resolvi tentar uma outra versão, aproveitar o domingo, dia 27 de agosto. Fazia algum tempo que queria desenferrujar meu Crystal e aprender o framework web chamado Lucky. Ótima desculpa. Deixei a documentação do site aberta de um lado, editor do outro e fui fazendo tentativas. Minha premissa é que Crystal, sendo uma linguagem que compila binário nativo, como Go, deveria ser bem mais rápido que minha versão em Rails. No final gastei o domingo inteiro mais a segunda feira seguinte pra fazer, porque nunca tinha usado Lucky antes.</p>
<p>Vamos anotar isso também pro próximo video: falar um pouco mais das minhas experiências mexendo em linguagens diferentes, mas pra hoje só precisa entender que os criadores do Crystal, o Ary e o Brian, quiseram inventar uma linguagem com sintaxe o mais próximo possível de Ruby, adicionando coisas modernas como inferência de tipos, fibers, channels, mas com perfil de performance similar de Go, e em alguns casos até de Rust.</p>
<p>Foi nesse ponto que fui vendo outros desenvolvedores na comunidade começarem a esmiuçar mais os critérios do ranking, em particular o MrPowerGamerBR. Sendo javeiro ele foi um dos que ficaram sofrendo bullying do povo tirando sarro do Java ter se saído tão mau no ranking. Em vez de só sair dando reply xingando todo mundo, como todo twiteiro faz, ele fez o que um programador de verdade deveria fazer: abrir o código e provar com números.</p>
<p>Ele fez a mesma coisa que eu: começou uma nova versão na linguagem que mais gosta: Kotlin. Pra quem não sabe, Kotlin foi inventado pela JetBrains, a empresa que faz a IDE IntelliJ e o Android Studio. Eu também gosto dela e vou deixar anotado pra comentar a respeito no próximo video. Pra hoje, basta entender que é compatível com Java e tecnicamente oferece a mesma performance.</p>
<p>Dia 28 de agosto ele postou essa versão, que não só melhora a posição de Java, mas iguala na mesma posição Top 1 da versão de Rust do Vinicius Fonseca. Mais do que isso, ele estudou mais a fundo o script de stress test em Gatling e determinou que existe um número máximo de requisições e de inserts possível durante os 3 minutos fixos do teste: 46 mil quinhentos e setenta e seis inserts.</p>
<p>Na verdade vai ser 46 mil quinhentos ou seissentos e alguma coisa porque no script de stress test do Gatling tem essa chamada pra &ldquo;randomized&rdquo; que afeta a quantidade de requisições pra criação de registros. Se rodar o teste várias vezes, vai dar números diferentes. Imagino que o Zanfranchesci fez isso de propósito pra adicionar o fator de &ldquo;sorte&rdquo; no espírito de uma rinha. Portanto o resultado numérico nunca foi pensado pra ser totalmente justo. E antes que os haters apareçam de novo: eu estou super ok com isso.</p>
<p>Mais do que isso, o MrPowerGamerBR olhou com mais calma a versão de Rust do Vinicius. Sim, ele estava inconformado, e nada é mais motivador do que a força do ódio. Ele notou que surgiam inconsistências esquisitas. Às vezes o total dava 300 a mais do que o máximo. Às vezes dava 500 a mais. Como isso poderia ser possível? Não tem como aparecer requisições do nada. Se existe um máximo e passa do máximo, obviamente existe um bug.</p>
<p>E de fato tem. Não foi de propósito, a gente esquece bugs. Por isso sempre falamos: não é porque uma linguagem compila, não é porque uma linguagem tem checagem de tipos, que não precisa de testes. No código do Vinicius tem uma rotina de aquecimento que insere mais de 500 registros falsos, não sabemos porque, mas depois tem um código que deleta isso. Só que esse código tem uma race condition, uma condição de corrida. Um erro clássico em programação concorrente. Ou seja, às vezes a rotina de limpeza roda antes de terminar de inserir tudo, aí sobra registros e isso polui o total no final.</p>
<p>Não chega a ser um problema, porque mesmo se corrigir esse bug, ainda assim a versão dele atinge o máximo, então sempre seria o primeiro. Mas estudando o stress test, estudando o código do Vinicius e também o do Lucas Weis que fez em Node.js, o MrPowerGamer aprendeu que existem truques que poderiam ser usados que a maioria não pensou. Não foram só eles, mas alguns pensaram além do óbvio e conseguiram resultados melhores.</p>
<p>Em 10 de setembro, o MrPowerGamerBR soltou um video detalhando essas descobertas e recomendo que assistam depois. Eu vou explicar tudo que ele falou e muito mais no próximo video. Uma dessas técnicas foi bulk inserts, a outra técnica foi gerar um índice especial pra pesquisas. Vamos deixar anotado aqui pro próximo video: explicar o que são bulk inserts e explicar como fazer pesquisas fuzzy no postgres e os segredos de ajustes de performance.</p>
<p>Se consertar o bug de race condition do código do Vinicius e se tirar o fator de aleatoriedade do stress test, o ranking fica bem diferente. Como o MrPowerGamer mostra nos seus tweets, o primeiro lugar seria compartilhado entre a versão de Rust do Vinicius, que continua sendo o primeiro, mas a versão de Go do Leonardo Vargas também sobe pra primeiro assim como o 6o lugar da Isadora e o 9o lugar do Navarro também virariam primeiro lugar.</p>
<p>Por causa disso o terceiro lugar de dotnet do André e Albert pula pro segundo lugar. Eles raspam no máximo mas não ultrapassam por meros sessenta e cinco inserts. O quinto lugar do Luiz Picanço em Rust sobe pro terceiro lugar. O Navarro, que tinha feito duas implementações em Rust, também sobe a outra versão, do 10o pra quarto. O oitavo lugar do Jean Rodrigues, em Go, sobe pra quinto lugar. Todo mundo vai subindo no ranking.</p>
<p>Mais ainda: sabendo o número máximo de inserts, se alguma versão ultrapassar, sabemos que tem bug nas validações. E isso aconteceu com o quarto lugar em Lean4 da Sofia e da Gabi, e a que ficou em sétimo lugar do Vinicius Santos, em Rust. Ambos ultrapassam os 48 mil e quinhentos e tanto, mas como tinha aquele fator de aleatoriedade no script, na rodada oficial, passaram batido, mas deveriam ter sido desclassificados. E que fique claro que não foi de propósito, que atire a primeira pedra quem nunca deixou um bug pra trás. Como já expliquei, numa rinha tem fator sorte, e está ok.</p>
<p>O problema é que o script de stress test, e o critério de ranking, não tinham sido divulgados desde o começo e nem testam tudo que está nas instruções. Por exemplo, eles deveriam checar as constraints da DDL do SQL que cria a tabela e índices pra garantir que coisas como apelido tem índice de unicidade, que o campo de data de nascimento era realmente de tipo data e coisas assim. Se fizer um campo de nascimento sendo um varchar, um string, e simplesmente inserir o que vier sem checar, no final vai ter mais registros do que deveria.</p>
<p>Se fosse pra ser mais rígido, deveriam ter implementado uma rotina de testes automatizados em Cypress, por exemplo, uma suite de testes de aceitação que testam todas as regras que estão nas instruções. Assim seria possível ver se a aplicação está devolvendo os códigos de erro quando enviamos dados inválidos. Sem isso, vários bugs passam despercebidos. Fica a dica pra uma próxima rinha. E também pra projetos de verdade: sem testes, tudo compila, tudo roda, mas bugs de validação vão passar despercebidos, em todas as linguagens, compiladas, tipadas, ou não.</p>
<p>Vou repetir: não encarem como uma crítica à rinha, o próprio nome &ldquo;rinha&rdquo; é pra ser uma brincadeira. Inclusive, eu acho que se tivesse um teste de aceitação mais rígido assim, teria muito menos participantes e muito mais projetos desclassificados, e meio que perderia a graça.</p>
<p>Mas o que me motivou a gastar tanto tempo nisso é que só olhando o ranking oficial, sem todo o contexto, eu também estava aceitando que Rails ou Java iam ser lentos mesmo e bola pra frente. Mas à medida que fui explorando o que o Vinicius, o Lucas, o Leandro, o MrPowerGamerBR, fizeram nas versões deles, resolvi copiar na minha versão de Crystal, pra tirar a prova dos nove. Não era pra ser difícil. Na minha cabeça, coisa de poucas horas.</p>
<p>Demorei um tanto pra entender o framework Lucky, o Avram que é o ORM que fala com o banco, tive até que abrir o código fonte deles pra entender. Na dúvida, na falta de documentação ou stackoverflow, nunca penso duas vezes e vou ler direto na fonte. Vantagem de usar projetos de código aberto é que o código é aberto, sacaram? É pra ler. E eu fiquei empolgado em tentar implementar o sistema de cache com job assíncrono pra fazer bulk insert como o Vinicius fez em Rust. Sendo Crystal compilado, era pra ter performance similar, né?</p>
<p>Ledo engano. Assim que terminei e fui testar com o Gatling, a decepção. Lembra a barrinha vermelha? Olha ela aqui firme e forte. Minha implementação nem entraria nos Top 20. Meros 20 mil inserts. Só que diferente do que tava pensando sobre Ruby, que é lento por ser interpretado e não tinha o que fazer, ou Java que come muita RAM e não tinha o que fazer, aqui eu fiquei meio puto. A culpa não podia ser do Crystal, nem do Lucky, tinha que ser minha. Eu estava fazendo alguma coisa muito errada. Será que eu sou uma farsa? Será que eu sou tão ruim assim?</p>
<p>Quando gastamos um tempo tentando descobrir um problema num código e depois de &ldquo;ver tudo&rdquo;, ou achar que viu tudo, ainda assim não encontrar, eu sempre tenho uma saída que funciona. Olhem neste código de Crystal. Pra passar o stress test, basta devolver HTTP 201 created e um Location válido pra próxima requisição. Até tinha deixado comentado aqui esse código de teste aqui. Posso descomentar essas duas linhas e comentar todas as de baixo. É isso mesmo, na dúvida, tire tudo e faça a resposta mais simples e idiota, devolver uma constante.</p>
<p>Mas esta Location não vai existir, porque se não inserir nada, não existe ninguém com ID número 1. Então, no show.cr, também tinha deixado esta linha comentada, que sequer tenta fazer a pesquisa no banco, só devolve outra constante. Descomento e comento o resto. Pro teste de consulta passar certo, basta devolver HTTP 200 de OK. Finalmente, na pesquisa por termos, mesma coisa, descomento esta linha pra devolver código 200 e comento o resto.</p>
<p>Só fazendo isso o teste de carga já passa. Esta vai ser a velocidade máxima  desse app, sem processar nada no banco, manjam? Estou removendo uma das variáveis de incerteza. Rodando o Gatling, só esperar 3 minutos e esse é o resultado. Tudo verde, nenhum vermelho de knockout, e olhem aqui do lado, todos os tempos de resposta da ordem de 1 milissegundo. Na verdade é menos de 1 milissegundo, mas ele arredonda. Esse é o tempo perfeito, porque a aplicação não tá fazendo praticamente nada. Ótimo, isso mostra como o Crystal sozinho é rápido.</p>
<p>Agora posso ir voltando meu código, uma linha de cada vez. Salvo, subo o docker-compose, que vai recompilar a imagem, e rodo o Gatling de novo. Checo e vejo se a diferença no resultado condiz com o código extra que coloquei. Volto, descomento mais uma linha, salvo, reinicio docker-compose, testo de novo, meço. E vou fazendo isso, uma linha de cada vez. E como esperado, nenhuma dessas coisas extras deu nada de estranho.</p>
<p>Até eu chegar nesta linha, a que monta a URL de Location. A única coisa que isso vai fazer é gerar uma linha como esta aqui em cima, um http://localhost etc. Claro, este método faz mais do que só concatenar strings, serve pra montar todo tipo de URL complexa que quiser incluindo encoding de parâmetros e tudo mais. Imagina aquelas URLs gigantes da Amazon. Mas eu nunca esperaria isso causar qualquer diferença significativa.</p>
<p>Na realidade, isso tá até errado. Pensei melhor e Location na realidade só precisa devolver o path, só a parte de &ldquo;/pessoas/id&rdquo;. Não precisa do protocolo nem domínio a menos que eu queira redirecionar o cliente consumindo a API pra outro domínio diferente, que não é o caso. Substituindo o método .url pra .path e testando de novo, batata, agora o resultado melhorou.</p>
<p>Eu twitei sobre isso, daí uma conta da comunidade Crystal retwitou e um dos desenvolvedores do framework Lucky, o Jeremy, ficou interessado. Ele foi tão legal que abriu uma issue no projeto. Fui lá explicar esses detalhes. Ele já tinha uma suspeita em mente e me pediu pra testar um ajuste. Este foi meu post no ticket aberto. Quando testei sem o patch dele, tava tomando knockouts, mas colocando o patch, caiu de mais de 600 knockouts pra menos de 10, então tinha alguma regressão de performance mesmo.</p>
<p>Não sei explicar exatamente o motivo ainda. A versão anterior parece que só concatenava strings, e o patch usa tipo um string builder. Concatenar gera duplicatas da string. Um Builder pré-aloca espaço e vai só adicionando as novas partes sem duplicar. É um trade off de CPU por RAM. Eu sei que isso faz alguma diferença, mas não sei ainda explicar porque faria diferença aqui. Mesmo tendo, não deveria ser significativo. Mas de qualquer forma, no número de stress test do Gatling fez muita diferença.</p>
<p>Sem querer, acabei achando um gargalo de performance no framework que levou a um conserto. É assim que a gente acaba contribuindo em projetos open source: sem querer. Mas essa não era a única otimização. Olhando outras versões notei que ninguém tava devolvendo mensagens de erro completas, como &ldquo;apelido já existe&rdquo; ou &ldquo;formato de data inválida&rdquo;. As instruções só falam que precisa devolver código de status 400 ou 422.</p>
<p>Na minha versão, antes, além do código eu devolvia um JSON com o erro. E processar JSON também custa tempo. Tirei todos os métodos de render ou json e usei só o método &ldquo;head&rdquo; que, como o nome diz, devolve só o cabeçalho, o código de erro. É uma coisa besta, que em testes individuais não faria diferença, mas o  Gatling manda centena de milhares de requisições. Aí parece que faz diferença.</p>
<p>Mesma coisa logs. O teste não exige logs. No mundo real precisamos ter mensagens de erro gravadas e retornadas pra ficar mais fácil achar bugs e defeitos, mas pra este desafio de performance que só dura 3 minutos, podemos desligar todos os logs e ganhar alguns milissegundos importantes. E fazendo pequenos ajustes assim, o resultado de inserts de Crystal foi subindo. Dos pífios 15 mil inserts, foi pra 30 mil, daí pra quase 40 mil inserts.</p>
<p>Não bati no máximo mas chegando perto dos 40 mil inserts, já foi um salto muito bom. Quando postei esse resultado já era dia 1o de setembro. Ainda me distraí no processo porque queria uma biblioteca pra auxiliar o caching em Redis mas não tinha nenhuma bem feito. Esbarrei num bem antigo chamado Kiwi que ninguém atualizava fazia tempo, então resolvi arrumar e melhorar o código dela. E essa foi minha segunda pequena contribuição open source.</p>
<p>Enquanto isso, em paralelo, o Leandro e o Lazaro, das versões de Ruby mínimo e de Ruby on Rails, continuaram mexendo nas versões deles. O lázaro que, no ranking oficial da rinha, tinha ficado lá na 19a posição com uma faixa de 24 mil inserts, também saiu ajustando as configurações dele e com muito pouco ajuste já tinha conseguido bater os 40 mil inserts também. O Leandro também. Com esses resultados eles já teriam entrado nos Top 10.</p>
<p>O Leandro experimentou mais com as configurações de postgres e de nginx. Muitos de nós achávamos que esses dois componentes não eram tão importantes. Pior, achávamos que eles não ajudavam em nada, então fizemos outra coisa feia que muitos fazem sem pensar: colocamos nginx pra aceitar o máximo de conexões quanto possível aumentando o número de worker connections lá pra cima de 10 mil. E no caso do Postgres a gente configurava os pools de conexão pra dezenas ou centenas de conexões. E isso é um enorme erro.</p>
<p>Vamos anotar pra explicar no próximo video, mas deixa eu tentar ilustrar aqui. Pra ter uma visão na cabeça vamos imaginar uma lanchonete, com vários caixas tirando pedido e mandando pros cozinheiros atrás. Os caixas são nossa aplicação, feitos em qualquer das nossas linguagens. Os cozinheiros são as conexões do banco de dados que é a cozinha.</p>
<p>O nginx é como o tamanho da porta da lanchonete. Se fizer uma porta que aguenta passar 100 mil pessoas de uma só vez, o que acontece? O stress test começa com poucas pessoas, umas 3, mas ao longo de 3 minutos, dispara 600 simultaneamente. Só que essas 600 não são atendidas ao mesmo tempo. Não tem caixa suficiente, daí vai formar um amontoado de gente desordenado.</p>
<p>Do lado da cozinha, o banco de dados, cada cozinheiro extra exige pelo menos uns 3 MB de RAM de espaço e vai consumir CPU. Mas a cozinha, é limitada. Lembra da regra da rinha? No máximo 1.5 CPU. Não adianta aumentar cozinheiro infinitamente, que a cozinha não aguenta. Se falarmos pros caixas tirarem quantos pedidos conseguirem, rapidamente esses pedidos vão ficar empilhados porque não tem cozinheiro suficiente pra atender.</p>
<p>A vontade é aumentar os cozinheiros, o pool de conexões. Digamos, temos duas instâncias da aplicação, os caixas, e cada um vai poder tirar até 100 pedidos de uma só vez. Então 200 pedidos. Mas não tem como ter 400 cozinheiros. Primeiro porque eles ocupam espaço, 3 mega cada já vai dar no mínimo 1.2 gigabytes de RAM. Mas beleza, o estabelecimento inteiro aguenta 3 gigabytes que é o limite imposto pela rinha lembra? Mas não tem fogão suficiente pra cozinhar, a cozinha tá apertada, temos menos de 1 CPU, cada cozinheiro vai demorar mais e mais tempo pra atender cada pedido. Entenderam?</p>
<p>Então o cenário é assim: a porta do estabelecimento permite entrar todo mundo de uma só vez, mil pessoas, 10 mil pessoas, entra tudo. Só tem 2 caixas, duas instâncias. Mesmo tirando pedidos rápido, fica tudo acumulado esperando. Não adianta contratar mais cozinheiro, porque a cozinha é limitada, e quanto mais cozinheiros tentamos colocar, mais cada cozinheiro demora. E enquanto o pedido não é atendido, a pessoa fica lá pendurada na frente do caixa esperando. E assim, vai acumulando centenas de pessoas na lanchonete esperando, até começarem a perder a paciência e ir embora. São as requisições perdidas ou knockouts.</p>
<p>A solução que o Leandro publicou não é intuitiva. Em vez de fazer o nginx aceitar quantas pessoas puder, fazemos diferente: limitamos o tamanho da porta. Digamos, no máximo 1000 pessoas. O NGINX coloca um sistema de senhas do lado de fora e vai deixando entrar só 1000 de cada vez, de forma mais ordenada. É igual num Poupa Tempo ou no seu banco. Pega a senha, senta e espera ser chamado. Agora os caixas atendem menos pessoas de uma vez, mais ordenado, e tiram menos pedidos de uma só vez, digamos, no máximo 50 em vez de 100 ou 200. Esse é o pool de conexões.</p>
<p>E na cozinha, em vez de precisarmos de 300 ou 400 cozinheiros, agora precisamos só de 100. Mas aí não vai andar mais devagar? Não, porque sobra mais espaço na cozinha e cada cozinheiro consegue cozinhar mais rápido. É melhor 100 cozinheiros terminando os pratos em 1 minuto ou 400 cozinheiros levando 10 minutos cada porque tão disputando fogão? Entenderam?</p>
<p>Eu estava cometendo esse erro também. Tava deixando nginx lá em cima, com portão gigante pra suportar 10 mil conexões e compensando com 200 conexões no pool de cada instância do Rails. E pra piorar ainda estava cometendo um erro no postgres. Eu tinha esta linha no docker-compose:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>volumes: - ./postgresql.conf:/docker-entrypoint-initdb.d/postgresql.conf</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso diz pro Docker Compose pegar esse arquivo postgresql.conf do meu diretório local e copiar pra dentro do container de postgres. Mas tava faltando modificar o comando de inicialização pra ter &ldquo;-c&rdquo; com o nome desse arquivo. É nesse arquivo que digo pro postgres que ele deve suportar, digamos, um máximo de 400 conexões.</p>
<p>Como não tava carregando o arquivo, subia com o limite padrão de 100 conexões. E isso confunde, porque eu pensava que ia conseguir abrir mais de 100, mas não ia. E não sabia porque. Esse foi o primeiro motivo de porque meus resultados tavam lá na casa dos 15 mil inserts: nesse caso faltava cozinheiro e eu achava que tinha suficiente. Falta de atenção que chama isso. Em vez de consertar no lugar certo, eu ficava tentando compensar em outros lugares, como no NGINX.</p>
<p>No próximo episódio vou explicar como eu poderia ter evitado essa confusão. Vamos deixar anotado aqui. Depois que descobri isso, e li o tweet do Leandro, reconfigurei o nginx pra faixa de 1000 workers de conexão e depois diminuí o número de cozinheiros no pool de conexões pra uns 40. O problema nem era o máximo de conexões, era a compensação excessiva que tava fazendo no NGINX. Um erro levando a outro erro.</p>
<p>À medida que ia diminuindo esses dois números, passava a aumentar a quantidade de inserts no final, a quantidade de pessoas atendidas de fato. Eu tava achando que minha versão de Rails, pelo fato de ter exagerado na solução e enfiado Sidekiq pra jobs, Redis pra fila e cache, ia faltar espaço na cozinha, mas não foi isso, foi questão de gerenciar o fluxo de pessoas.</p>
<p>Limitar a porta e os caixas em vez de escancarar. Lembrem-se disso: atendimento não é tentar atender todo mundo de uma só vez, é atender grupos organizados de cada vez. É uma questão de vazão e fluxo. Isso é essencial em operações. Seja atendimento numa lanchonete, seja numa linha de fábrica, seja num servidor web.</p>
<p>E foi assim que em 3 de setembro, postei o resultado da minha versão de Rails. Faixa de 39 mil inserts, quase batendo os 40 mil que queria. O cenário que descrevi de gerenciar o fluxo de pessoas na lanchonete é um caso clássico, que demonstra que o problema não é a capacidade de atendimento do caixa, ou de cozinhar dos cozinheiros: é de gerenciar o fluxo de requisições, das pessoas ou sessões. É quando sabemos que o problema principal é gerenciar I/O, entrada e saída.</p>
<p>Nós que somos mais experientes já deveríamos ter parado pra pensar nisso primeiro, mas assumimos errado no começo e essa hipótese errada nos fez perder muito tempo. Assumimos que o problema seria a cozinha, o banco de dados. Aí tentamos compensar aumentando número de cozinheiros ou cozinheiros mais rápidos que seriam os caches. Seria o caso se o problema fosse CPU bound, limitação de CPU. Mas na realidade era problema de fluxo, de entrada e saída, no fluxo dos caixas, o problema era I/O bound, como a maioria dos problemas de Web comum.</p>
<p>Uma vez controlado o fluxo, podemos otimizar o caixa pra em vez de só tirar o pedido e ficar esperando de braços cruzados esperando; enquanto o cozinheiro tá ocupado, o caixa pode fazer outras coisas na frente, como receber o pagamento, pegar a bandeja, separar o refrigerante, limpar o balcão. Não precisa esperar o prato chegar pra depois fazer tudo isso, dá pra fazer em paralelo. É pra isso que servem threads ou os async/await de várias linguagens, como Javascript no Node.js.</p>
<p>Nesse ponto eu já tinha visto o MrPowerGamer e outros colaboradores como o Bruno Borges conseguindo subir Kotlin e Java pro mesmo nível do Rust. Já vi o Leandro e o Lazaro subindo Ruby e Rails pra perto do mesmo nível, faixa dos 40 mil inserts. Eu mesmo já tinha conseguido subir Crystal e Rails. Foi quando minha intuição dizia que a maioria dos participantes, se tivessem conhecimento de tudo que a gente descobriu até agora, seriam capazes de chegar perto desses 40 mil inserts ou até bater o máximo dos 46 mil e quinhentos do Rust do Vinicius.</p>
<p>Alguns não gostaram de ouvir isso, porque agora aquele bullying contra o Java ou a fanboyisse de Rust perderiam valor. E como eu detesto dizer uma coisa sem ter provado antes, resolvi fazer a única coisa racional: demonstrar na prática. Ainda era a mesma segunda-feira, 4 de setembro. Escolhi começar aleatoriamente mexendo na versão em Node.js e Express do Lukas Weis que já tava perto do topo, na faixa de 34 mil inserts, em 13o lugar no ranking oficial. Na minha máquina tava dando abaixo disso, uns 27 mil.</p>
<p>A versão dele foi uma das que mexi mais. Como foi um código feito pra competição, ele e muitos outros não se preocuparam em seguir boas práticas, clean code, nem nada disso, só cuspiram código até funcionar e publicaram. Não tem absolutamente nada de errado nisso e não considerem esses códigos como representação do que eles conseguem fazer com mais tempo, em projeto de verdade. Isso tem que ser repetido o tempo todo: código pra competição é diferente de código de verdade.</p>
<p>Mas como eu estava com tempo e não era pra participar da competição, aproveitei pra desenferrujar meu Javascript e fui refatorar um pouco o código pra ficar mais organizado e mais legível. Meu objetivo era totalmente diferente da rinha: dar uma limpada e otimizar o suficiente pra bater pelo menos a faixa dos 40 mil inserts, o que elevaria o projeto pros Top 5 oficiais.</p>
<p>Recomendo que leiam meu pull request com calma, deixei o link na descrição do video abaixo, mas em resumo, depois de organizar o código comecei tirando fora a otimização prematura de usar Redis como cache. Como falei antes, o problema não era a velocidade dos cozinheiros, o postgres. Portanto a solução não era tentar achar cozinheiro mais rápido, um Redis. Com a configuração adequada do fluxo de nginx, do pool por postgres, dividindo CPU e RAM melhor no Docker Compose, já resolvia. Arranquei o Redis fora, daí teria mais recursos pro postgres.</p>
<p>Esse cache, além de tudo, não estava sendo efetivo porque o Lucas tentava cachear os resultados do endpoint de pesquisa por termos, só que o stress test manda pesquisas com termos aleatórios, raramente repetidos, então o cache nunca ia ajudar, porque toda pesquisa ia ser nova e não ia ter o resultado no cache. Portanto cache na pesquisa de termos era inútil, só um peso a mais de gravar no cache.</p>
<p>Além disso, no endpoint de criar novo registro, ele deixava o banco de dados criar a chave primária, daí recebia de volta no final e só aí gravava no cache. Isso tem que acontecer um atrás do outro, não dá pra ser assíncrono. Adicionar o valor no cache só aumentava o tempo da requisição. Mais uma vez, era um trabalho extra sem benefício.</p>
<p>No endpoint seguinte, de pesquisar pessoa direto por ID, ele buscava no cache. Mas a pesquisa por chave primária no postgres é igualmente rápido. De novo, o cache não fazia diferença. Só faria sentido se primeiro gerar a chave primária, com uma biblioteca de UUID na aplicação, e mandar o insert pro banco já com essa ID e em paralelo já mandar gravar no cache também com a mesma ID. Como não precisa esperar o banco devolver uma nova ID, não precisamos esperar pra gravar no cache, entenderam? É uma das vantagens de usar um gerador de chaves primárias externo ao banco. Muito iniciante não sabe que não é obrigatório deixar o banco responsável por IDs.</p>
<p>O Lucas também tava devolvendo códigos de erro HTTP junto com mensagens em JSON descrevendo o erro, mas a rinha não se incomodava com isso, então só arranquei fora. O stress test não liga pra mensagem, só quer respostas rápidas. No mundo real, o certo é dar respostas completas, porque sem logs bem descritos depois fica difícil achar bugs e problemas. Novamente, é uma otimização que só serve pra uma competição.</p>
<p>O Node.js tem recurso de cluster, subir forks do processo pra escalar melhor. Mesma coisa que Rails também faz. O Lucas parece que experimentou, mas desistiu. Eu ia tirar fora, mas voltei e passei a usar pra tentar distribuir um pouco mais a carga. Ter um reactor com corrotinas assíncronas e conseguir ter concorrência é diferente de ter paralelismo e o ideal é ter as duas coisas. Eu explico isso no video de concorrência e paralelismo e no próximo video talvez eu tente explicar melhor neste contexto. Vamos deixar anotado.</p>
<p>A parte que possivelmente pesou mais na versão do Lucas foi o endpoint de pesquisa que faz select LIKE na tabela. Muitos erraram nisso: precisa fazer select LIKE nas colunas apelido, nome e stack, então é apelido LIKE termo OR nome LIKE termo OR stack like termo, e esse é o pior jeito de fazer select. O melhor jeito é pré-criar um único campo com as três colunas concatenados durante o insert. Daí fazer um índice usando GIST com a extensão de trigrams. Alguns tentaram GIN, mas pra este desafio o melhor era GIST. Já tínhamos anotado, mas vou explicar isso no próximo video.</p>
<p>A segunda coisa foram as validações. Validar os dados antes de inserir no banco costuma ser mais rápido do que tentar inserir e deixar o banco devolver erro. Por exemplo, que a data de nascimento é inválido, tipo mês 13 ou dia 31 em fevereiro. Podemos só checar se a string tem o formato de ano, tracinho, mês, tracinho dia, no mínimo. Ou tentar converter essa string num objeto válido de data pra garantir. Eu não medi exatamente esse ponto, mas o Lucas usou a biblioteca moment.js, que antigamente já foi bem popular mas acho que é considerada meio obsoleta.</p>
<p>O jeito mais popular hoje é usar outra biblioteca, a date-fns ou simplesmente usar o construtor da classe Date do Javascript nativo. Eu comecei tentando usar o date-fns mas achei mais fácil só checar se o parse da string pra um new Date is not a number (isNaN). Com todas essas mexidas fui mexer no Docker Compose. Ele tava subindo 3 instâncias de Node no Docker, mas como reativei o código de Node Cluster, desci pra 2, cada uma com 1 fork extra, totalizando 4 processos.</p>
<p>Diminuindo a carga do código de cache, que não tava servindo pra nada. Melhorando as pesquisas por termo. Diminuindo a carga do código de validação. Ou seja, fazendo nossos caixas trabalhar menos, em seguida era o problema de diminuir a montanha de gente que o nginx tava deixando entrar tudo de uma só vez. Diminuí a quantidade de workers do nginx dos absurdos 20 mil, pra 10 mil até 1024. Notei que uns 1000 é ideal pra maioria das aplicações. Muitos até menos.</p>
<p>Fazendo isso os caixas trabalham de forma mais organizada e eficiente, precisando de menos cozinheiros, menos conexões de banco no pool. O Lucas tinha configurado com 200, mas agora dava pra descer pra uns 45, talvez até menos. E fazendo tudo isso, essa versão de Node.js saiu da faixa de 27 mil inserts, na minha máquina, ou 34 mil que conseguiu na máquina dos organizadores da rinha, e pulou pra faixa dos 40 mil também. Do meu ponto de vista, foi uma mudança significativa. E cimentou na minha cabeça que dava pra fazer a mesma coisa pra outras versões.</p>
<p>Desculpas ao Lucas por ter pego ele pra Judas, mas não entendam errado. Vários outros, incluindo eu mesmo, cometemos os mesmos erros. Só aproveitei pra explicar mais na versão dele pra não me repetir em todos os próximos. Enfim, ainda no dia 4 de setembro, depois de atingir os 40 mil inserts com o Node.js, resolvi mexer em outro velho conhecido, Elixir. Na minha cabeça não fazia sentido Elixir ter se saído abaixo dos Top 10. Mas eu fiquei surpreso com um problema: essa versão do Gabriel Oliveira se recusava a carregar na minha máquina!</p>
<p>Como chegou a figurar no ranking oficial, significa que o Zan conseguiu rodar pra avaliar. Portanto, se a versão é a mesma (e tem que ser, é a mesma imagem de Docker), por exclusão, tem que ser alguma coisa na minha máquina. Mas toda vez que eu tentava subir o Docker Compose, via as instâncias da app crasheando sem nenhuma mensagem de erro, simplesmente morriam. Puf, zero, nada.</p>
<p>Sem entender o motivo, fiz um teste: mudei o comando do docker compose pra só subir um  &ldquo;sleep infinity&rdquo; do Linux. Todo mundo conhece o conceito de sleep, certo? Pausa o processamento pelo tempo que mandar. Linux suporta infinity, daí fica pausado pra sempre, como um loop infinito, while true da vida. Fazendo isso o container fica de pé pra eu poder fuçar. Daí é possível usar o comando docker-compose exec pra abrir um bash dentro do container.</p>
<p>De dentro, tentei executar o binário do app na mão, e de fato, crasheava. Achei isso muito estranho, aí me ocorreu, será que o app é muito pesado? Vou tentar carregar o iex que é só a linha de comando do REPL do Elixir, sem carregar nada da aplicação. E crasheava também. Estranhíssimo. Vamos pular o Elixir e carregar direto só o Erlang, o comando &ldquo;erl&rdquo;. E crasheava! Bizarro! A sensação que eu fiquei é que os programas pediam mais recurso do que a máquina conseguia entregar e crasheavam, como falta de RAM.</p>
<p>Resolvi editar o arquivo de docker compose pra tirar a limitação da rinha de CPU e RAM. Tentando rodar de novo, aí subia! Por alguma razão tava faltando RAM. Abri o HTOP pra ver quanto de RAM tava usando e era um absurdo, coisa de quase 1 gigabyte e meio! Fiquei bem confuso. Como fazia tempo que não mexia com Elixir fiquei pensando, &ldquo;ué, será que nos últimos anos eles enfiaram tanta coisa no Elixir que agora precisa de mais memória que uma JVM do Java?&rdquo;. É confuso porque quem já mexeu com web de Java, com Spring e tudo, sabe que 1GB de RAM não é fora do comum. Mas não esperava isso do Elixir.</p>
<p>Eu tava quase jogando a toalha, então resolvi reportar no meu Twitter. Todo mundo achou estranho, mas o próprio Gabriel conseguiu achar a resposta, era o limite máximo de porta que o Erlang detectava no meu sistema. Em Erlang e portanto também em Elixir, portas são usadas pra se conectar com o mundo externo. Por exemplo, pra ler do standard input, STDIN, ou escrever no standard output, STDOUT, são portas. Pra abrir arquivos, pra receber conexões de rede, são portas.</p>
<p>Erlang pré-aloca no mínimo 1024 portas. Pra determinar o máximo, podemos abrir o shell &ldquo;erl&rdquo; e rodar o comando <code>erlang:system_info(port_limit).</code> E olha só: meu sistema devolve mais de 1 milhão de portas. Daí tem o detalhe que ainda não entendi: na aplicação em Elixir parece que tenta pré-alocar esse máximo sei lá porque, em vez de ir só alocando se precisar. Daí dá mais de 1 gigabyte de RAM. E se limito dentro das restrições da rinha, iria faltar memória no container, crasheando na inicialização.</p>
<p>Felizmente, com uma simples variável de ambiente <code>ERL_MAX_PORTS</code>, podemos colocar um número mais sano, tipo 2048 ou algo assim. Limitando de 1 milhão pra 2 mil, fez uma puta diferença, agora o app sobe, consumindo na faixa de 160 megabytes por instância, que é bem mais razoável e próximo até de versões como Rails ou Node.</p>
<p>Isso é um problema que só aconteceu no meu sistema porque a configuração de Arch Linux é mais agressiva nos limites do que um Debian ou Ubuntu, que são mais conservadores. Sendo mais específico, isso é limite de file descriptors, ulimit do sistema, depois pesquisem sobre isso. De qualquer forma, a versão Elixir do Gabriel eu gostei bastante porque usou características exclusivas de Elixir. Ele fez as duas instâncias rodando em containers diferentes se conectarem num cluster.</p>
<p>Clusters saem de graça em Erlang, porque a máquina virtual já trás toda a infraestrutura pronta. É fácil de usar. Não serve pra tudo, mas quando precisa é bem útil. Lembram como falei que não valia a pena usar um cache como Redis, primeiro porque o postgres já era rápido o suficiente, mas também porque subir um container de Redis significa dar menos recursos pro postgres? Com o recurso de cluster do Erlang, dá pra ter um cache compartilhado entre as duas instâncias, sem precisar de um Redis entre os dois.</p>
<p>O app em Elixir consegue consultar na memória da primeira instância, e a segunda instância no outro container, consegue pedir pra primeira instância porque estão no mesmo cluster. Assim não precisam de uma memória externa na forma de um Redis da vida. À primeira vista isso parece mais esperto, mas como já disse antes, o problema não é o postgres, e adicionar um sistema de cache só adiciona peso desnecessário.</p>
<p>Mesmo assim esta versão, ajustando só nginx e o pool de conexões, consegue passar dos 41 mil inserts e entra nos Top 5. Mas tem um porém, além do cache, as instâncias faziam chamadas RPC entre eles, ou seja a 1a instância podia pedir pra 2a instância gravar ou pesquisar no banco e vice versa. Isso eu achei desnecessário e adiciona uma comunicação externa via rede pra toda chamada.</p>
<p>Eu não tive tempo de testar, acho que se arrancar fora essas chamadas remotas, o código de cache e dar mais recursos pro postgres, talvez seja possível conseguir um resultado melhor. Mesmo assim eu esperava que Elixir conseguisse um número maior. De todas as versões que eu mexi, essa ainda foi a que performou um pouco pior. Então tem espaço pra simplificar mais o código. Elixir certamente consegue mais. Estão notando o padrão: em vez de adicionar coisa, estou tirando coisas e com isso deixando mais rápido. Por isso sempre repetimos tanto que otimização prematura é a raíz de todo o mal.</p>
<p>Com a versão Elixir do Gabriel batendo acima dos 40 mil inserts, resolvi procurar uma versão em Go Lang. E escolhi um dos que ficou mais embaixo no ranking. A do Luan ficou em 23o lugar com só 21 mil inserts. Assim como Elixir, não tem porque Go ficar lá embaixo e isso já se provou porque pelo menos 3 versões tinham alcançado os Top 10.</p>
<p>Fiz pequenos ajustes no docker compose, pra dar um pouco menos de CPU pras instâncias de app, assumindo que Go sem nenhum framework iria precisar de menos recursos que Elixir. Daí podemos aumentar CPU do postgres. Teve duas modificações de código que quis mexer. Primeiro, fazer erros devolverem só os códigos de erro 400 ou 422 de HTTP sem mensagens de erro. É pouca coisa mas já faz um pouco de diferença.</p>
<p>A segunda coisa é que ele tava usando Regular Expressions pra validar o formato da data de nascimento. Na real, acho que isso é meio besteira, mas como Regex costuma ser mais lento do que dar split na string e checar cada componente, troquei também. Acho que nenhuma dessas duas coisas faz muita diferença no final. O que deu diferença sim foi o que já falei antes. Primeiro, diminuir o tamanho da porta de entrada: diminuir os 10 mil workers de nginx pra 1024. E aumentar o pool de conexões, embora isso também nem tenha sido necessário.</p>
<p>Só de limitar e controlar o fluxo de entrada de requisições, literalmente uma linha, já disparou o resultado da faixa de 29 mil inserts pra 34 mil. Não chegou nos 40 mil que eu queria, mas por hora resolvi subir meu pull request, que também tem link na descrição abaixo, e pular pra outra coisa. Só pra terminar esse dia 4 de setembro, fui dar uma fuçada na tal versão em dotnet do André e Albert que alcançaram terceiro lugar.</p>
<p>Como muitos outros, eles também optaram por fazer uma versão codificada de forma rápida, sem muitas boas práticas, então ficou bem verbose, tudo super longo. A curiosidade é que como muitos,  também optaram por implementar um cache, mas diferente da maioria, em vez de Redis escolheram usar Nats.</p>
<p>Nats deveria ser mais usado, e parece que povo de Dotnet usa mais. É uma alternativa até mais confiável do que Redis pras funções de cache ou como uma fila de jobs simples. Em cima do nats, usaram os recursos de async do C# com channels pra fazer a implementação de bulk insert. E com isso conseguiram atingir quase o máximo, 44 mil inserts. Esta versão não mexi em nada, só fucei e tweetei a respeito.</p>
<p>No dia seguinte, dia 5 de setembro, me sugeriram fuçar alguma versão de PHP então puxei pra rodar a versão do Lauro Appelt que ficou em 17o lugar no ranking, com meros 25 mil inserts. Fazia muitos anos que não mexia com PHP, então precisei da ajuda dos universitários.</p>
<p>Pacotes de PHP hoje em dia se instalam com Composer, que é parecido com NPM de Javascript ou Cargo de Rust. Foram-se os dias de baixar zip de sites como Freshmeat ou SourceForge. Mas PHP ainda tem o conceito de extensions, que como o nome diz, carrega junto com o PHP em si, e modificam as estruturas internas da linguagem.</p>
<p>Antigamente lembro de usar PEAR que eram as extensions em C, mas hoje parece que são PECL. Nesse estágio precisava carregar essa nova extension chamada Swoole, que adiciona funcionalidades de corrotinas e modelo de I/O não-bloqueante, o que transforma o PHP em algo parecido com Node.js. Basta baixar, compilar e instalar a extension e editar o php.ini pra carregar ela na inicialização.</p>
<p>Depois de bater cabeça pra entender isso e instalar os pacotes, pude rodar essa versão do Lauro, que ele implementou com o framework Hyperf. PHP 7 tem uma sintaxe bem mais moderna do que o antigo PHP 3 que eu usava, mais de 20 anos atrás. Mudou bastante coisa, tá bem mais legível, bem mais organizado. E o Lauro foi um dos poucos que deu uma caprichada e deixou tudo fácil de entender e legível. Recomendo darem uma olhada.</p>
<p>Nessa versão também não mexi em nada no código, só parei pra entender, desenferrujar um pouco do meu PHP. A única coisa que ajustei foi o pool de conexões. Aqui, ao contrário da maioria, o Lauro foi super conservador e limitou o pool de conexões com 10 conexões, muito pouco. Resolvi soltar tudo e jogar lá pra cima, em 500, que é exagerado. Mas fazendo isso e rodando o stress test, essa versão também subiu pra quase 40 mil inserts, o que também o colocaria lá pelos Top 5.</p>
<p>Por causa disso já me dei por satisfeito por enquanto, já que queria pular pra ver outras versões. Nesse dia não mexi em muita coisa. Dei uma pausa e a noite resolvi que ia mexer mais no dia seguinte, mas acabei não conseguindo dormir. Levantei e voltei pro PC. Queria mexer em pelo menos mais uma versão, a de Python. Escolhi o 21o lugar do Ian Cambrea, com menos de 24 mil inserts.</p>
<p>Ele não fez em Django, que seria o mais comum pra web em geral, nem em FastAPI que parece ser o mais comum pra APIs. Ele escolheu Sanic, que é um framework que eu não conhecia. A única modificação significativa que eu fiz foi notar que ele também resolveu usar Redis pra cache, mas não implementou um pool de conexões pro Redis. Tava usando uma única conexão pra tudo.</p>
<p>Não consigo afirmar que era só isso, mas testando na minha máquina como os outros, o resultado bateu nos 40 mil inserts também. De curiosidade, essa foi a única versão que eu vi que usou NGINX diferente. Normalmente a gente configura NGINX pra fazer balanceamento de carga usando protocolo HTTP, portanto usando rede TCP por baixo, fazendo proxy reverso pra outro servidor web HTTP atrás, no caso o Sanic.</p>
<p>Mas NGINX, e outros serviços que rodam em Linux, como o próprio Postgres, que também costumamos conectar via TCP na porta 5432, suportam conexões usando UNIX Sockets. Se tudo for rodar na mesma máquina, que não é comum no mundo real, não tem necessidade de usar rede TCP/IP. Tá tudo na mesma máquina. Podemos fazer um processo se comunicar diretamente com outro processo usando IPC ou inter-process communication, e um dos jeitos de fazer isso é via UNIX Sockets.</p>
<p>De forma resumida, sabe quando fazemos tipo um cat num arquivo, adicionamos um Pipe, e rodamos um grep do outro lado pra filtrar o conteúdo do arquivo? Isso é um jeito do processo grep se comunicar com o processo cat via esse pipe que liga o stdout do cat ao stdin do grep. É mais ou menos isso que acontece com o NGINX usando unix sockets pra falar com as instâncias de Sanic. Basta todos enxergarem os mesmos arquivos .sock. Depois dêem uma olhada no repositório do Ian pra ver como ele fez.</p>
<p>Como a versão de Python também foi fácil e não exigiu nenhum trabalho meu além de ajustes triviais, fui dormir e no dia seguinte resolvi olhar pro topo da lista de novo. Fiquei curioso com isso de Lean4 que a Sofia e a Gabi usaram e alcançaram o Top 4 do ranking. Nunca tinha ouvido falar nessa linguagem, mas o que vi foi impressionante.</p>
<p>Lean4 é uma linguagem experimental ainda, longe de ser 1.0, criada na Microsoft Research pelo brasileiro Leonardo de Moura em 2013. O objetivo dele foi criar uma linguagem pra ser fácil criar provas de teoremas. É uma linguagem mais focada em formalidades matemáticas, inspirada em linguagens funcionais como Haskell ou OCaML. Ele tem um cheiro de dialeto de linguagem da família ML. Se curte linguagens funcionais e formalidade matemática, essa pode ser uma boa alternativa.</p>
<p>Mais impressionante foi a dupla Algebraic, as autoras do projeto da rinha, a Sofia que tem só 21 anos e a Gabi que tem só 17 ou 18 anos. Como falei desde o começo, implementar esses endpoints de API, em si, não é nenhum desafio. Mas neste caso foi. Lean4 é experimental, comunidade pequena, e quase nada pra Web, porque nunca foi o foco. Ela é quase toda focada em teoremas matemáticos e não criar APIs pra Web.</p>
<p>Pra fazer APIs, elas precisavam de um parser de JSON, já que na regra da rinha, o stress test ia mandar milhares de requisições com JSON. Mas não tinha uma biblioteca de JSON, então a Sofia fez um do zero. Quando se recebe as requisições HTTP, algumas contendo esses JSON, precisa conseguir parsear o pacote. Existe já parser de HTTP em C, que todo mundo usa em várias linguagens, mas pra Lean4 não tinha. Então a sofia fez um wrapper em FFI pra integrar.</p>
<p>Pra implementar a aplicação em si, ela queria usar algum framework web, como em Node você tem Express ou em Java tem Spring. Mas em Lean4 não tinha, então a Sofia fez um pequeno do zero.  Além disso, pra rodar uma aplicação web, duh, precisa de um servidor Web. Sabe? Tipo Node.js, tipo um web2py, ou um Tomcat. E claro, também não tinha. E claro, a Sofia fez um. Ou seja, esse foi aquele caso de &ldquo;não sabendo que era impossível, foi lá e fez&rdquo;.</p>
<p>Só esse esforço já merece uma menção honrosa, porque eu acho que ninguém teve metade do trabalho que elas tiveram. Mais foda ainda porque essa versão foi uma das mais performáticas. Também é igualmente impressionante que uma linguagem desconhecida, experimental e que provavelmente ninguém nunca usou pra web, seja tão performática.</p>
<p>O criador, Leonardo, focou em fazer uma linguagem com objetivo de provar teoremas e verificação formal em vez de performance bruta. Mesmo assim foi bem sucedido em performance. Não deixem de olhar o código da rinha no repositório da Gabi, além das bibliotecas no repositório da Sofia. Links abaixo.</p>
<p>No video de análise do MrPowerGamerBR, onde ele descobriu os limites do stress test e das regras da rinha, também conseguiu identificar que algumas das versões nos Top 10 tinham bugs, como de validação, que acabaram deixando passar inserts que não deveriam, em particular por falta de validação de data.</p>
<p>Essa versão em Lean4 tinha esse tipo de bug. Elas esqueceram de colocar validação da data de nascimento. Tudo bem, mesmo com a validação, a versão delas ainda ia ficar entre os Top 10, por pouco. Então aproveitei pra tentar molhar meus dedos em Lean4 pela primeira vez e implementei uma validação simples. Foi o código menos intuitivo que eu fiz em muito tempo. Acredito que quem for mais experiente em linguagens como Haskell ou F# devem conseguir entender mais fácil.</p>
<p>Tinha outro pequeno bugzinho na hora de gerar a URL pro cabeçalho de Location depois de inserir o registro no banco, então corrigi isso também. De qualquer forma, vejam o link dos meus pull requests na seção de links abaixo. No fim desse dia postei um tweet de resumo até este momento com tudo que descobri. Neste estágio da minha saga, já tinha conseguido fazer minha própria versão de Rails e de Crystal bater os 40 mil inserts, ajustei a versão em Node do Lucas, Elixir do Gabriel, Go do Luan, PHP do Lauro, Python do Ian, além dos ajustes no Lean da Sofia e Gabi.</p>
<p>Chegamos no dia 6 de setembro, véspera de feriado prolongado de Independência e que eu tinha planejado viajar&hellip; Depois de ter batido cabeça com Lean4, resolvi que queria bater cabeça com outra linguagem que nunca mexi antes. Tentei fuçar Zig, mas achei que tava experimental demais, o ferramental super instável, na verdade, tudo instável. Coisas da versão 0.10.0 quebram na 0.12.0. Pra uma experiência de um dia só, não ia compensar. Eu acho que é uma das linguagens mais promissoras, tanto que foi usada pra fazer o novo Bun, mas vou deixar pra outra ocasião.</p>
<p>Então lembrei que sempre quis tentar a linguagem Nim, outra que é compilada como Go, tem uma sintaxe inspirada em Python, existe já faz alguns anos então assumi que seria mais estável e melhor documentada do que Zig. Provavelmente fácil de chegar numa boa performance, assim como consegui com Crystal. Ledo engano. Apanhei bastante, a começar pela falta de documentação.</p>
<p>Não ajudou nada que a biblioteca assíncrona pra Postgres se chama asyncpg, que é o mesmo nome da de Python. Quando pesquisa no Google, adivinha quem aparece primeiro? Pra piorar, a linguagem e suas bibliotecas usam e abusam de macros, que modificam a sintaxe da linguagem. Sem ter uma boa experiência, foi confuso diferenciar o que era da linguagem e o que eram macros. As mensagens de erro do compilador confundiam mais do que ajudavam. Reclamações de amador, claro, não estou dizendo que é um defeito, mas é uma curva de aprendizado maior pra iniciantes.</p>
<p>Recursos de corrotinas, tipos como Option, pareciam ainda meio experimentais, com hacks pra funcionar certas sintaxes. Escolhi o framework web Jesper ,que parece um Sinatra de Ruby. Apanhei um bocado mas consegui implementar os 4 endpoints numa manhã. Só que contrário às minhas expectativas, o resultado foi pífio, uma verdadeira droga mesmo.</p>
<p>Já tentei compilar com opção speed, nada. Já tentei compilar com modo multithread, nada. Suspeito que a biblioteca de async pool que estou usando é imatura e vaza conexões. Depois preciso investigar isso melhor. Mesmo aumentando pool pra 100 ou mais, nem chega até o fim do stress test, abre o bico muito antes. Male male insere 2000 inserts. Isso deixaria esta versão lá no fim do ranking oficial, só ganhando da versão de brincadeira feita em Bash do Leandro.</p>
<p>A probabilidade maior é que eu, sendo amador, posso ter feito erros de iniciantes e por isso foi ruim. Mas na maioria das linguagens mais maduras, um iniciante teria dificuldade de deixar tão lento assim, mesmo se fosse num Javascript. Se tiver alguém assistindo que tem experiência com Nim, não deixe de dar uma olhada no meu pull request linkado abaixo pra ver se descobre o problema, mas no estado que tá agora, eu não recomendaria usar Nim pra uma aplicação web, mesmo se fosse bem simples. Se for pra usar uma linguagem imatura com bibliotecas instáveis e mau testadas, é melhor ir direto pra Zig ou V Lang que são mais modernas.</p>
<p>Apesar dessa tentativa frustrada, outros desenvolvedores acompanhando minha saga começaram a se mexer pra fazer versões melhores. Por exemplo, nesse mesmo dia vi o Junior Leão mostrando uma outra versão em Java com Spring Boot, usando Redis pra cache com JDBC direto e também conseguiu alcançar os 40 mil inserts.</p>
<p>Outra tentativa bem sucedida foi do Carlos Silva, conhecido como Insalubre, que tinha participado com uma versão em V Lang, mas tinha algum problema e ele foi desclassificado, então nem entrou no ranking oficial. V Lang é uma nova linguagem com cara de Go. Ele se inspirou nas discussões de técnicas que ficamos fazendo no Twitter, consertou a versão dele, e atingiu os 46 mil inserts.</p>
<p>O Leandro continuou mexendo na sua versão de Ruby e neste dia   reportou que conseguiu bater os 46 mil inserts. Ele foi quem mais testou a hipótese de diminuir a porta de entrada do NGINX e controlar a vazão de requisições. Com só 256 workers do NGINX e 30 conexões totais de Postgres no pool, conseguiu alcançar os 46 mil inserts, demonstrando que nem o nginx e nem o postgres nunca foram os gargalos desta rinha. Veja o fio do tweet dele nos links abaixo pra mais detalhes.</p>
<p>No fim do dia resumi num tweet todas as técnicas que descobrimos até agora. Por exemplo, validação pode consumir  tempo precioso, quanto mais otimizar, melhor. Não precisa devolver mensagens de erro elaboradas, muito menos em JSON, só o cabeçalho HTTP com o código de erro tá otimo e vai ser mais rápido. O postgres não é o gargalo, não com essa quantidade mixuruca de dados. 50 mil inserts não é nem um peido. Por isso não precisa tentar otimizar prematuramente com Redis pra cache.</p>
<p>Linguagens que escalam com threads ou forks precisam de mais conexões com o banco. Um exclusivo pra cada thread. Então tem que dar um pouco mais de RAM pro banco subir essas conexões extras. Cada conexão de Postgres custa caro, uns 2 a 3 megabytes, porque são forks. Então 100 conexões exigem no mínimo 300 megabytes. E ainda tem que sobrar RAM pra processar as queries. Ou seja, menos de 500 megabytes é inviável. Linguagens com suporte a fibers exigem um pouco menos conexões, porque compartilham a mesma conexão na mesma thread. De novo, tá anotado já pra eu explicar isso no próximo episódio.</p>
<p>Uma coisa que eu e o MrPowerGamerBR vimos na implementação vencedora em Rust do Vinicius Fonseca, é que ele entendeu mas nem todo mundo pensou na forma correta de lidar com searches fuzzy do Postgres. Também já anotamos pra eu explicar no próximo episódio. O Vinicius fez certinho. Ao tornar o search mais eficiente, sobra mais CPU pro postgres conseguir lidar com os inserts.</p>
<p>E por fim, o mais importante, é lembrar do conceito de controlar a vazão na entrada, diminuindo quantidade de workers de NGINX. Esse nunca foi o gargalo, com meio CPU e quase nada de RAM,  aguenta todas as requisições do stress test sem reclamar. Alguns não gostam de NGINX porque algo usando FastHTTP de Go é muito mais eficiente, mas nesta rinha o NGINX nem chega perto de ser gargalo.</p>
<p>Colocando 0.15 CPU e uns 200 megabytes pro NGINX, perto de 0.75 CPU e pelo menos 1.2 GB de RAM pro Postgres, daria pra colocar um pool exagerado de mais de 200 conexões, embora os 100 padrão no total seja suficiente pra maioria.
O que sobrar, dá pra dividir com sua aplicação, ou seja, 0.3 CPU e até 0.8 GB de RAM pra cada instância.
Depende se sua aplicação for mais pesada que isso, como as versões em Java ou Rails ou mesmo Node, aí precisa ir tirando e testando de 0.05 em 0.05 do postgres, dando pra aplicação e medindo pra ver os limites.</p>
<p>E chegamos no feriadão de 7 de setembro. Apesar de eu estar coçando pra investigar mais códigos da rinha, tive que parar por alguns dias. Eu corri pra tentar desvendar tudo antes do feriado, e apesar de ter conseguido ajustar várias versões pra chegar nos 40 mil inserts, ainda tava coçando a cabeça com o elusivo máximo de 46 mil. Tinha algumas suspeitas pra testar quando voltasse, mas fui viajar com essa pulga atrás da orelha ainda.</p>
<p>Já tinha planejado de levar minha namorada pra conhecer Gramado, no Rio Grande do Sul. Nos divertimos bastante apesar de estar super cheio e com tempo meio duvidoso. Visitamos diversas atrações, como a Super Carros, onde pude fazer test driver de Ferrari e Nissam GT-R! Mas, este video não é de turismo. Quem me acompanhou no Instagram viu as stories em tempo real. Se não acompanhou e ficou curioso, deixei um destaque lá. Procurem o link na descrição abaixo.</p>
<p>Mesmo durante o feriado, vários desenvolvedores continuaram empenhados em desvendar os mistérios da Rinha e na sexta feira, dia 8 de setembro, outro dos participantes, o Vinicius Ferraz, fez umaa descoberta muito importante. Ele twitou assim: &ldquo;você que participou da rinha, tomou um j.i.IOException: Premature Close? Tunou o banco, sua app, o nginx e não conseguiu resolver 100%? Sabe onde poderia estar o problema? Na rede do docker. Como resolver? network_mode: host em todos os serviços.</p>
<p>Caraca!!! Nas primeiras versões que mexi, na minha em Rails, em Crystal, a de Node, onde gastei mais horas, chegava perto dos 40 mil, 39 mil, o comportamento ficava estranho. Olhando no gráfico não tinha muita explicação. Ficava esse tremido esquisito no fim do gráfico, como se estivesse tentando ultrapassar um teto invisível.</p>
<p>Mesmo nas versões vencedoras, como o Rust do Vinicius, dava pra ver um tremido menor no fim. Mas ele conseguia ir aguentar até o fim. Independente da versão, mais cedo ou mais tarde, aparecia isso. Eram requisições que começavam a se perder! Não perdia tudo nos inserts, mas principalmente nas pesquisas por termo, até em pesquisas inválidas, o que era mais absurdo, já que  só checa se o parâmetro de termo não existe e devolve cabeçalho de erro. Esse endpoint nunca tinha que dar knockout, porque não processa nada.</p>
<p>Como teve aquele caso do Elixir do Gabriel, que descobrimos que meu sistema Manjaro baseado em ArchLinux tem ulimit maior e por isso afetava o uso de memória do Erlang, imaginei que tinha alguma coisa na minha infra que também pudesse estar afetando isso. Cheguei a suspeitar do Podman, que eu estava usando no lugar do Docker. Mas não deu tempo pra investigar isso porque chegou o feriadão e fui obrigado a parar.</p>
<p>E era mesmo no Docker, e o mesmo problema acontece no Podman. Explicando. Quando subimos container de Docker, não é uma máquina virtual. Eu expliquei isso em detalhes no meu video de Docker, então assistam lá depois. O processo roda nativo no sistema operacional host. Só que a infra do RunC de Linux, que é a base do Docker e do Podman, nos dá opção de enganar o processo rodando. Uma dessas formas é quando mapeamos volumes. Dizemos que um diretório dentro do container na verdade mapeia pra outro diretório do lado de fora, no host.</p>
<p>O mesmo vale pra rede. Digamos que eu queira subir vários postgres na mesma máquina. Não ia conseguir, porque o primeiro ia subir e dar bind na porta 5432. O segundo ia tentar dar bind na mesma porta, mas como já tá ocupado, ia falhar. Com containers, cada processo consegue dar bind na porta 5432 de uma rede falsa, virtual. Ele acha que tá sozinho na máquina. Daí podemos mapear a porta interna do container pra uma porta na rede de verdade do lado de fora.</p>
<p>Pra isso precisamos de uma ponte de rede, um &ldquo;bridge&rdquo;, que é o modo de rede ou network mode padrão. Mas podemos escolher fazer bind direto na porta da rede de verdade, do host. É isso que o Vinicius sugeriu: network_mode: host. Ao não usar o modo ponte, estamos retirando o gargalo da tradução de tráfego da rede virtual pra rede de verdade. Isso custa recursos, é mais pesado. Não existe almoço grátis. Tudo custa alguma coisa. O que a gente não sabia é que era pesado a ponto de afetar esse stress test.</p>
<p>Sem querer, esse era o elo perdido da rinha, a última peça que faltava. E só pra me deixar ainda mais ansioso pra voltar do feriadão, no domingo dia 10 o desenvolvedor Reinaldo resolveu fazer uma versão nova em Java usando Quarkus, Reactive e VertX seguindo as dicas do Bruno Borges e Vinicius Ferraz. Depois vejam o repositório dele, o usuário é zsantana. Ele twitou como conseguiu uma versão que atingia mais de 51 mil inserts.</p>
<p>Mas já falamos que o máximo é 46 mil e quinhentos, portanto certamente esta versão tem bug de validações, como eu demonstrei na versão em Lean4 da Sofia. Mais importante, esta versão em Java atingia um critério que a rinha não media mas que pra mim era importante e eu passei os últimos dias tentando entender porque ninguém atingia: Zero Knockouts. Não perder nenhuma requisição. Esse é o ponto onde vencemos o stress test.</p>
<p>Entenderam? Até este momento a noção era que o stress test era tão pesado que nenhuma aplicação conseguia segurar tudo e eventualmente ia perder algumas requisições. Alguns mais, alguns menos. Isso é comum em testes de carga. Mas assim como nginx e postgres nunca foram o gargalo, a carga também nunca foi tão pesada assim. Quando o Zan foi testar os participantes pra encontrar os finalistas, o teste era ainda mais leve.</p>
<p>Ele dobrou o teste, senão todo mundo ia passar. Mas mesmo dobrando, ainda não é pesado o suficiente. Todos os teste que fiz até agora foi com essa versão já dobrada. É importante ter essas ordens de grandeza em mente. Vamos deixar anotado porque preciso explicar isso pra vocês, o que significa sessões simultâneas, tempo de resposta, throughput.</p>
<p>Faixa de 50 mil linhas numa tabela é super leve pra um banco de dados como Postgres. Só vamos sentir que ele tá pedindo água quando atingirmos faixa de milhões de linhas e isso se estiver sem índices adequados. 600 sessões fazendo requisições, não é pesado. Mesmo com a restrição de menos de 1 CPU no Docker Compose. 1000 sessões simultâneas continua não sendo pesado.</p>
<p>A viagem de volta de Gramado foi cansativa. Varamos noite no domingo pra segunda. Estava de carro alugado e saímos quase 2h da manhã de Gramado pra devolver o carro às 4h em Porto Alegre e pegar o vôo de volta pra Congonhas, em São Paulo às 6h. Pegamos táxi perto das 9h da manhã. Antes das 10h chegamos em casa. Eu só pude cochilar rapidinho no avião e quando cheguei já liguei meu PC.</p>
<p>A primeira versão que queria testar foi a de Crystal com Lucky. Troquei o network mode no docker compose pra host e &hellip; foi! Era isso. Bateu 47 mil inserts. Provavelmente algum bug de validação porque ultrapassou o máximo. Zero knockouts. Sabe a tal tremida nos gráficos que falei? Liso, até o fim, uma linha reta em todos os gráficos. Era isso que eu sabia que Crystal tinha capacidade, e confirmamos, a rede do Docker que não estava deixando!</p>
<p>Próxima versão, mexi no meu Rails overengineered com Sidekiq e tudo mais. Network mode host e bateu acima de 45 mil inserts, faltou pouco pra atingir o máximo dos 46 mil e tantos. Não gastei muito tempo ajustando valores no docker composer, mas mesmo assim deu só 4% de knockouts. Antes dava 20%, 30% ou mais. Mas isso prova meu ponto: o gargalo nunca foi nem falta de CPU e nem falta de RAM, nem postgres e nem nginx, era um problema de I/O e isso é gerenciável em quase qualquer linguagem. Se Crystal e Ruby batem o máximo, qualquer outra deveria conseguir.</p>
<p>Peguei a versão de Lean4 e ainda tem algum bug de validação, mas bate acima dos 47 mil inserts. Mais uma versão que vence o stress test sem suar. Peguei a versão em Node.js do Lucas e também bateu no máximo de 46 mil e tantos. E isso foi se repetindo. A versão em Python e Sanic do Ian Cambrea, a versão em V Lang do Carlos, a versão em Go do Luan, a de PHP com Swoole do Lauro, a outra versão em Rails do Lazaro, todos batem o máximo.</p>
<p>De bônus, resolvi testar a versão em C++ do Lucas William que tinha ficado em 12o lugar com menos de 35 mil inserts, também bate perto do máximo agora, 43 mil inserts. Não bateu por algum bug na validação. Enquanto algumas versões faltava validação e deixava inserir mais que o máximo, nesta versão tem alguma coisa filtrando demais e impedindo de gravar mais. Olhando os gráficos claramente podemos ver que tá sobrando recursos. Zero knockouts, não tá perdendo requisições, então tem recursos sobrando.</p>
<p>Nesta segunda-feira, tava todo mundo só falando do lançamento da versão 1.0 do Bun, a nova alternativa a Node.js e Deno que promete ser mais performático. Ainda é cedo pra dizer e ainda nem é totalmente compatível. Mas o Yuri Gomes tinha participado da rinha com a versão pré-lançamento do Bun, 0.8 e tinha ficado em 14o lugar com menos de 28 mil inserts. Mas com network mode em host, ele também bateu o máximo e com tempos similares da versão de Rust. Neste exemplo com escopo pequeno, seu desempenho foi à altura do hype, o que é bem promissor.</p>
<p>Alguns ficaram me cutucando pra mexer em C# também. Eu não tinha mexido em nenhum até agora porque a versão do André e do Albert já provaram que alcançava o máximo. Mas, nada mais justo do que finalizar essa segunda-feira super corrida pós-feriado, do que avaliar a versão do nosso querido organizador da rinha, Zanfranceschi. Por coincidência ele participou com uma versão de Dotnet.</p>
<p>Não vi ele no ranking oficial. Acho que, como organizador ele se absteve de se colocar na disputa, participou pela diversão mesmo. Mas cometeu o mesmo erro que eu e outros: assumimos que postgres ia ser o gargalo e colocamos cache de Redis antes de realmente medir a necessidade. Mas espero que a esta altura tenha ficado claro que postgres nunca foi o gargalo. Não estou dizendo que nunca é, em outros projetos pode ser, mas neste desafio não era. Por isso é caso a caso, precisa medir.</p>
<p>Por causa disso ele implementou um cache em memória usando um ConcurrentDictionary. Lembra aquelas pesquisas com select LIKE que mencionei antes? No Postgres, se indexarmos com GIST e trigrams, fica bem rápido, mas com uma estrutura rudimentar como um dicionário, um hash, ele é obrigado a percorrer 100% do dicionário o tempo todo, extremamente lento.</p>
<p>Isso limitou o resultado a 40 mil inserts. Eu tentei fazer os mesmos ajustes de todo mundo: limitar vazão do nginx, mexer no pool de conexões, dividir melhor recursos do docker composer, colocar em network mode host, mesmo assim não dava. Minha suspeita é que esse ConcurrentDictionary matou esta versão, se alguém quiser tentar tirar e retestar, vá no repositório dele nos links abaixo.</p>
<p>De qualquer forma, com a ajuda de todo mundo, do Leandro, do MrPowerGamer, do Gabriel Oliveira, do Vinicius Ferraz e outros, conseguimos atingir o objetivo não oficial que eu estabeleci pra rinha: vencer o stress test e colocar todas as linguagens no mesmo 1o lugar ou dentro dos Top 3. Fizemos isso com 3 versões de Ruby, Node.js, PHP, Python, Java e Kotlin, Bun, V Lang, Go Lang, Crystal, C++, todo mundo bateu os máximos.</p>
<p>As duas exceções que não bateram foi a de C# do Zan e a de Elixir do Gabriel. Em ambos os casos, acho que a lógica de cache engargalou. Que é lição aprendida: não faça otimização prematura. Em vez de ficar mais rápido, pode ficar mais lento. Se tirar esse cache, provavelmente bateriam os Top 3. E falando em Top 1, agora que consegui cumprir minha missão nesta rinha, só faltava uma última coisa que queria fazer: desenferrujar meu Rust mexendo na versão do ganhador oficial, o Vinicius Fonseca. Finalmente chegamos no 16o dia da minha saga, dia 13 de setembro.</p>
<p>Assim como a versão em C++ do Lucas, ou em Node do outro Lucas, que colocaram tudo num arquivo só. O Vinicius fez a mesma coisa em Rust. Repetindo: é uma rinha, o critério não era o código durar pra sempre, era só aguentar o stress test. Beleza do código, mantenabilidade, legibilidade, segurança, nada disso eram critérios. Eu que fui narigudo e fiquei fuçando e mexendo no código dos outros sem ninguém pedir. Meu objetivo era só desenferrujar meus skills. Uma boa forma de fazer isso era pegar o código dos outros e sair reorganizando.</p>
<p>Essa foi a primeira coisa que fiz no código do Vinicius: refatorar. Rust já não é uma linguagem muito fácil de bater o olho. Cheio de anotações, um monte de .clone() e .unwraps() e tipos e generics pra lá e pra cá. E tudo amontoado. Dividindo em módulos fica mais fácil de bater o olho. Mas eu queria corrigir o bug que o MrPowerGamerBR identificou na análise dele: a condição de corrida que acabava deixando sobrar mais linhas na tabela do que o máximo possível.</p>
<p>Isso acontece porque o Vinicius manda inserir mais de 500 linhas na inicialização pra algum tipo de aquecimento. Que eu saiba o Rust não tem e nem precisa de um just in time compiler, um JIT, já que é pré-compilado em binário. Aquecimento faz mais sentido pra Java, que tem JIT. Enfim, ele deixa essa rotina rodando numa corrotina da biblioteca Tokio, a mais famosa que dá suporte à semântica de Futures, o equivalente a Promises de Node.js.</p>
<p>Na sequência, inicia um segundo Future assíncrono. Essa rotina tem um timer, um sleep de 2 segundos. Quando passa 2 segundos  manda um comando de SQL pra deletar as linhas de aquecimento. É aqui que acontece a condição de corrida. Nada garante que 2 segundos é suficiente pra inserir essas 500 linhas. Muitas vezes o delete acontece antes. Daí sobra linhas na tabela.</p>
<p>Não havia necessidade do delete estar num future separado. Bastava disparar o delete depois do último insert. Ou uma gambiarra seria aumentar esse timer de 2 pra mais segundos. Eu fiz isso. E pra garantir, alterei o último endpoint, o de contagem final da tabela, pra filtrar os registros de aquecimento. Com isso batemos os mesmos 46 mil e tantos inserts, como todo mundo. Depois dêem uma olhada no meu pull request pra conseguir entender melhor essa versão de Rust.</p>
<p>Eu não curto competições de código. Não porque sou contra. Cada um com seu entretenimento. Não tem nada de errado em competir com código. Só não pode achar que código de competição é a mesma coisa que código pra projeto de verdade. As técnicas são diferentes. Os requerimentos são diferentes. Muitos truques que mencionei, não devem ser usadas sem critério, vai dar errado. E nenhuma comparação generalizada deve ser feita entre as diferentes tecnologias usadas.</p>
<p>Uma competição no estilo dessa rinha serve pra mostrar quem foi mais esperto, quem teve mais sorte, quem foi mais cuidadoso. O Zan teve bastante trabalho pra organizar e pra avaliar um a um. Cada rodada do stress test do Gatling custa 3 minutos. 50 projetos que passaram significa que ele gastou quase 3 horas só esperando os testes rodarem, sem contar os outros projetos participantes que tinham bugs. Agora que passou é fácil dizer &ldquo;ah, poderia ter feito X melhor, poderia ter feito Y melhor&rdquo;, mas a posteriori tudo é fácil.</p>
<p>Eu gastei mais de 2 semanas avaliando 18 repositórios em 16 linguagens diferentes. Eu tive a sorte de contar com a contribuição de diversos outros desenvolvedores que se animaram pra investigar junto. Acho que conseguimos desvendar todos os truques possíveis desta rinha, incluindo vencer o stress test do Gatling. Mas isso não é competição, é análise técnica. Não é divertido pra todo mundo, só pra nós mais nerds.</p>
<p>Mesmo se você for torcedor de um Neymar, Messi, Cristiano Ronaldo, seja lá quem acha que é o melhor jogador do mundo, se no dia do jogo eles torcerem o tornozelo e não participarem, o resultado vai ser o que der no dia. Quando perdem, não significa que são ruins no geral. Só significa que perderam aquele jogo, ponto final. É o conjunto da obra que determina os melhores e não um jogo individual. Sorte faz parte do jogo. Mesma coisa nesta rinha. Só porque um Java ou um PHP não apareceram no topo, desta vez, não tem nenhuma correlação com a qualidade deles em outros jogos.</p>
<p>Amador desbocado é quem faz mais barulho nas redes sociais, então meu objetivo foi educacional: demonstrar porque estão errados e fazer todos entenderem que sim: com mais tempo, experiência, skill, qualquer uma das linguagens tinha capacidade de alcançar resultados semelhantes, alguns com mais trabalho, alguns com menos trabalho, alguns gastando mais recursos, alguns menos, não importa. Sua linguagem não te define.</p>
<p>Assistiram meus videos de Sua Linguagem Não é Especial? O que eu falei lá? Um bom desenvolvedor é promíscuo com linguagens. Eu mexo em qualquer uma, a qualquer hora, quando quiser e como quiser. O que eu falei em videos como Aprendendo a Aprender ou Aprendizado na Beira do Caos? Várias das linguagens que mexi nesta saga, ou nunca tinha mexido, como Nim, Lean4, V Lang, ou tava enferrujado fazia anos, como C++, C#, Rust, Go, PHP, Crystal. Mesmo linguagens que tenho um pouco mais de intimidade, como Ruby, Javascript, Python, tive que quebrar a cabeça.</p>
<p>O que eu fiz? Instalei uma a uma e fui fuçar código dos outros. Quebrei elas várias vezes. Consertei, refatorei, reorganizei, fiquei em muita tentativa e erro, reimplementei técnicas diferentes, apliquei técnicas de uma linguagem em outra, li muito código fonte. Mesmo sem nunca ter mexido num framework como Lucky, mesmo Bun que acabou de lançar, não levou mais que algumas horas pra me acostumar. Essa é a diferença: as primeiras linguagens que aprender vai levar meses, anos pra dominar.</p>
<p>As próximas vão ficando exponencialmente mais rápido de pegar, o suficiente pra conseguir fazer o básico. Novamente, demonstrei este ponto na prática. Em um dia já dá pra fazer alguma coisa, se me der poucos dias, consigo me virar em qualquer uma. Em poucas semanas já vou saber truques avançados de cada uma. Eu consigo me tornar produtivo numa linguagem nova em 1 mês. Onde vou levar mais tempo? Em linguagens que tem paradigmas muito diferentes, como Haskell, Ocaml, F#, Clojure ou o Lean4 que vimos hoje.</p>
<p>Com todos esses disclaimers e avisos feitos, ouçam até o final antes de sair comentando. Sim, Rust e Go aparecem no topo do ranking oficial porque, se não cometer erros graves, o tempo de resposta individual de cada requisição vai ser na faixa abaixo de 1 milissegundo, de microssegundos. Frameworks como Rails ou um Laravel da vida vão responder na faixa de milissegundos. Quanto menor o tempo de resposta, mais requisições dá pra responder no mesmo período de tempo. Rust ou Go realmente tem mais velocidade bruta e conseguiram bater a barreira acima dos 40 mil inserts, mesmo com docker em modo ponte, e chegar no máximo durante o evento.</p>
<p>Porque todo mundo não passa a adotar Rust, Go, V Lang, Zig, de uma vez? Tudo compilado em binário nativo super rápido? Vamos colocar assim: não existe um único motivo. Não pensem que as pessoas ou empresas são burras e não sabem que são rápidos. C e C++ existem muito antes da maioria de vocês nascerem e sempre foram rápido. Todo mundo sabe disso.</p>
<p>No caso de um V Lang ou Zig é fácil explicar: é porque são novos demais e instáveis demais, tem suporte baixo, tem ferramental imaturo, mudando o tempo todo e quebrando compatibilidade, falta documentação na forma de livros, cursos justamente porque ainda estão mudando muito. Esses não tem como ser adotados em larga escala. Só early adopters muito dedicados. Só empresas menores e com maior margem de assumir riscos, com um co-fundador ou diretor técnico muito bom pra liderar.</p>
<p>Mas tudo é uma questão econômica. Não me xinguem, eu sou só mensageiro, mas a realidade é que a enorme maioria dos programadores não tem capacidade de aprender a usar linguagens de baixo nível, no nível que seria necessário pra serem produtivos e produzir código bem feito. O fato da maioria das empresas não estar desesperadamente procurando esse perfil é a evidência. Sempre existem exceções, não comece com &ldquo;ah, mas eu conheço fulano ou ciclano&rdquo;. Exceção não define a regra.</p>
<p>Fica a dica: se tornar um programador avançado nas tecnologias mais difíceis, te torna uma mosca branca, que vale muito mais
que os outros. O que eu sempre falo? Tudo que é fácil pra você aprender, também é fácil pra qualquer outro. Quanto mais gente tiver fazendo a mesma coisa, mais barato todos vão ser. O valor está em ser bom naquilo que poucos tem capacidade de conseguir. Onde a barreira é mais alta. E isso vai ser Rust, Go, C++, pra resolver problemas muito mais complicados do que fazer Cruds ou APIs.</p>
<p>Reflitam sobre vocês mesmos, porque estão estudando Java ou C# ou Javascript? Porque são as posições que tem mais vagas. Rust não se vê vagas todos os dias. Estão vendo? É o problema do ovo e da galinha. Você não quer investir tempo pra aprender Rust porque não vê vagas todo dia. E as empresas não oferecem vagas porque não vêem trocentos cursos e estudantes aprendendo. Por isso que esse tipo de movimento, ou começa em startups arrojadas ou dentro de gigantes como uma Microsoft. As vagas existem, bem pouco, e pagam bem, só que são bem mais exigentes. Mas ninguém gosta de vagas difíceis, só querem as que acham fácil.</p>
<p>Não é prático nem realista manter aplicações web complexas e gigantes em linguagens assim. Imagine um Shopify, GitHub, MercadoLivre, Amazon ou iFood da vida, 100% em linguagens de baixo nível. Partes do sistema, que são mais sensíveis a performance, já adotam coisas como Go, mas é a menor parte. Pro uso do dia a dia, dashboard, relatórios, cruds e coisas assim, não compensam. Seria um enorme desperdício dar tarefas consideras commodity como fazer relatórios, pra um tipo de programador raro e caro, com de Go ou Rust.</p>
<p>Adotar uma tecnologia só porque é mais rápida, não é um bom argumento. Hoje em dia todas são rápidas o suficiente. Mesmo linguagens que um programador de Rust chamaria de lentas, como Javascript ou Python, conseguimos atingir patamares similares com um custo um pouco maior de máquina. Como demonstrei aqui, na maioria dos casos, é possível dobrar a performance, triplicar, tendo o conhecimento adequado, sem quase modificar código, nem precisar jogar tudo fora e reescrever tudo em Rust. É isso que programadores como eu fazemos.</p>
<p>No fim dia, dado a escassez de programadores de Rust ou Go e a dificuldade de achar talentos que consigam ser treinados nisso, em pouco tempo, sai bem mais barato compensar com servidores mais parrudos. Alguns pensam que pra dobrar a performance da sua aplicação em PHP, em Java, Dotnet, Python, Node, precisa jogar tudo fora e reescrever em Go. Tá errado.</p>
<p>Só vai garantir que os erros por ignorância que foram cometidos com um PHP, vão se repetir em Go. Programador ruim, vai fazer código ruim em PHP ou em Go. Não existe nenhuma garantia que vai ficar mais rápido se você continua não sabendo porque o anterior tava lento. Por outro lado, por experiência, já melhorei muito sistema de verdade mexendo nos 20% de problemas que dão 80% de retorno. É sempre assim, às vezes são besteiras de pouquíssimas linhas, como as que mostrei durante este video.</p>
<p>Como falei antes, são vários motivos, custo-benefício sendo o principal. No geral o maior problema é que é muito difícil formar bons programadores que consigam tirar vantagem dessas tecnologias. O custo-benefício não compensa. Não significa que essas linguagens não tem chance, significa que esse mercado sempre vai ser de nicho. Mas pra quem tem o talento e a capacidade, é um nicho que paga muito melhor. É só ser bom.</p>
<p>Enfim, hoje eu só quis resumir minha saga de como explorei 16 linguagens, claro que superficialmente, em 16 dias. Não deu pra detalhar cada truque, mas deixamos tudo anotado, e no próximo video vou explicar o truque por trás da mágica: como tudo isso funciona, por que funciona e, mais importante, quando usar e quando não usar. Se ficaram com dúvidas mande nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal e não deixem de compartilhar o video com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>performance</category><category>linguagens-de-programacao</category><category>backend</category><category>akitando</category></item><item><title>[Akitando] #136 - Python? Java? Rust? Qual a Diferença? | Discutindo Linguagens</title><link>https://akitaonrails.github.io/2023/01/18/akitando-136-python-java-rust-qual-a-diferenca-discutindo-linguagens/</link><guid isPermaLink="true">https://akitaonrails.github.io/2023/01/18/akitando-136-python-java-rust-qual-a-diferenca-discutindo-linguagens/</guid><pubDate>Wed, 18 Jan 2023 08:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/MI9cdxETA4c"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Finalmente vou falar sobre linguagens que você goste ou use, mas vou fazer isso do meu jeito: escovando bits e explicando como muita coisa funciona por baixo de Python, Javascript e outras linguagens que talvez você não sabia antes de concluir na segunda metade onde discuto onde cada linguagem pode ser melhor aproveitada e porque.&lt;/p&gt;
&lt;h2&gt;Capítulos&lt;span class="hx:absolute hx:-mt-20" id="capítulos"&gt;&lt;/span&gt;
&lt;a href="#cap%c3%adtulos" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;00:00 - Intro&lt;/li&gt;
&lt;li&gt;01:36 - Cap 1 - Perl e Regex | Anos 90&lt;/li&gt;
&lt;li&gt;06:18 - Cap 2 - Estilo C e ICU | strftime&lt;/li&gt;
&lt;li&gt;10:25 - Cap 3 - Tudo de Python é em C! | Linguagem &amp;ldquo;Grude&amp;rdquo;&lt;/li&gt;
&lt;li&gt;17:54 - Cap 4 - Tudo de Node.js é em C! | LibUV&lt;/li&gt;
&lt;li&gt;22:31 - Cap 5 - Compilado vs Interpretado | ABI de C&lt;/li&gt;
&lt;li&gt;28:21 - Cap 6 - Interoperabilidade: Marshalling/Unmarshalling | FFI&lt;/li&gt;
&lt;li&gt;36:21 - Cap 7 - Onde cada Linguagem é mais Forte? | Rust&lt;/li&gt;
&lt;li&gt;38:09 - Cap 8 - Sistemas Distribuídos | Apache Java&lt;/li&gt;
&lt;li&gt;42:41 - Cap 9 - Produtividade &amp;gt; Performance? | Apps Comerciais&lt;/li&gt;
&lt;li&gt;48:31 - Bloopers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;span class="hx:absolute hx:-mt-20" id="links"&gt;&lt;/span&gt;
&lt;a href="#links" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/python/cpython/search?p=2&amp;amp;q=ifdef&amp;#43;MS_WINDOWS"target="_blank" rel="noopener"&gt;https://github.com/python/cpython/search?p=2&amp;q=ifdef+MS_WINDOWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python/cpython/blob/a87c46eab3c306b1c5b8a072b7b30ac2c50651c0/Modules/_ssl.c"target="_blank" rel="noopener"&gt;https://github.com/python/cpython/blob/a87c46eab3c306b1c5b8a072b7b30ac2c50651c0/Modules/_ssl.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pandas-dev/pandas/blob/main/requirements-dev.txt"target="_blank" rel="noopener"&gt;https://github.com/pandas-dev/pandas/blob/main/requirements-dev.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/numpy/numpy/wiki/Building-with-MSVC"target="_blank" rel="noopener"&gt;https://github.com/numpy/numpy/wiki/Building-with-MSVC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/scipy/scipy"target="_blank" rel="noopener"&gt;https://github.com/scipy/scipy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter/notebook"target="_blank" rel="noopener"&gt;https://github.com/jupyter/notebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openjdk/jdk/search?l=c%2B%2B&amp;amp;p=99"target="_blank" rel="noopener"&gt;https://github.com/openjdk/jdk/search?l=c%2B%2B&amp;p=99&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/libuv/libuv"target="_blank" rel="noopener"&gt;https://github.com/libuv/libuv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tensorflow/tensorflow"target="_blank" rel="noopener"&gt;https://github.com/tensorflow/tensorflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tensorflow/tfjs"target="_blank" rel="noopener"&gt;https://github.com/tensorflow/tfjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pytorch/pytorch"target="_blank" rel="noopener"&gt;https://github.com/pytorch/pytorch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/node/search?q=libuv"target="_blank" rel="noopener"&gt;https://github.com/nodejs/node/search?q=libuv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/async-await-retry"target="_blank" rel="noopener"&gt;https://www.npmjs.com/package/async-await-retry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/11996988/how-good-is-oniguruma-compared-to-other-cross-platform-regexp-libraries"target="_blank" rel="noopener"&gt;https://stackoverflow.com/questions/11996988/how-good-is-oniguruma-compared-to-other-cross-platform-regexp-libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/jni"target="_blank" rel="noopener"&gt;https://www.baeldung.com/jni&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.elastic.co/blog/found-zookeeper-king-of-coordination"target="_blank" rel="noopener"&gt;https://www.elastic.co/blog/found-zookeeper-king-of-coordination&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/akitaonrails/ObjC_Rubyfication"target="_blank" rel="noopener"&gt;https://github.com/akitaonrails/ObjC_Rubyfication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zaiste.net/posts/shell-commands-rust/#"target="_blank" rel="noopener"&gt;https://zaiste.net/posts/shell-commands-rust/#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SCRIPT&lt;span class="hx:absolute hx:-mt-20" id="script"&gt;&lt;/span&gt;
&lt;a href="#script" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Olá pessoal, Fabio Akita&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/MI9cdxETA4c"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Finalmente vou falar sobre linguagens que você goste ou use, mas vou fazer isso do meu jeito: escovando bits e explicando como muita coisa funciona por baixo de Python, Javascript e outras linguagens que talvez você não sabia antes de concluir na segunda metade onde discuto onde cada linguagem pode ser melhor aproveitada e porque.</p>
<h2>Capítulos<span class="hx:absolute hx:-mt-20" id="capítulos"></span>
    <a href="#cap%c3%adtulos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00 - Intro</li>
<li>01:36 - Cap 1 - Perl e Regex | Anos 90</li>
<li>06:18 - Cap 2 - Estilo C e ICU | strftime</li>
<li>10:25 - Cap 3 - Tudo de Python é em C! | Linguagem &ldquo;Grude&rdquo;</li>
<li>17:54 - Cap 4 - Tudo de Node.js é em C! | LibUV</li>
<li>22:31 - Cap 5 - Compilado vs Interpretado | ABI de C</li>
<li>28:21 - Cap 6 - Interoperabilidade: Marshalling/Unmarshalling | FFI</li>
<li>36:21 - Cap 7 - Onde cada Linguagem é mais Forte? | Rust</li>
<li>38:09 - Cap 8 - Sistemas Distribuídos | Apache Java</li>
<li>42:41 - Cap 9 - Produtividade &gt; Performance? | Apps Comerciais</li>
<li>48:31 - Bloopers</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="https://github.com/python/cpython/search?p=2&amp;q=ifdef&#43;MS_WINDOWS"target="_blank" rel="noopener">https://github.com/python/cpython/search?p=2&q=ifdef+MS_WINDOWS</a></li>
<li><a href="https://github.com/python/cpython/blob/a87c46eab3c306b1c5b8a072b7b30ac2c50651c0/Modules/_ssl.c"target="_blank" rel="noopener">https://github.com/python/cpython/blob/a87c46eab3c306b1c5b8a072b7b30ac2c50651c0/Modules/_ssl.c</a></li>
<li><a href="https://github.com/pandas-dev/pandas/blob/main/requirements-dev.txt"target="_blank" rel="noopener">https://github.com/pandas-dev/pandas/blob/main/requirements-dev.txt</a></li>
<li><a href="https://github.com/numpy/numpy/wiki/Building-with-MSVC"target="_blank" rel="noopener">https://github.com/numpy/numpy/wiki/Building-with-MSVC</a></li>
<li><a href="https://github.com/scipy/scipy"target="_blank" rel="noopener">https://github.com/scipy/scipy</a></li>
<li><a href="https://github.com/jupyter/notebook"target="_blank" rel="noopener">https://github.com/jupyter/notebook</a></li>
<li><a href="https://github.com/openjdk/jdk/search?l=c%2B%2B&amp;p=99"target="_blank" rel="noopener">https://github.com/openjdk/jdk/search?l=c%2B%2B&p=99</a></li>
<li><a href="https://github.com/libuv/libuv"target="_blank" rel="noopener">https://github.com/libuv/libuv</a></li>
<li><a href="https://github.com/tensorflow/tensorflow"target="_blank" rel="noopener">https://github.com/tensorflow/tensorflow</a></li>
<li><a href="https://github.com/tensorflow/tfjs"target="_blank" rel="noopener">https://github.com/tensorflow/tfjs</a></li>
<li><a href="https://github.com/pytorch/pytorch"target="_blank" rel="noopener">https://github.com/pytorch/pytorch</a></li>
<li><a href="https://github.com/nodejs/node/search?q=libuv"target="_blank" rel="noopener">https://github.com/nodejs/node/search?q=libuv</a></li>
<li><a href="https://www.npmjs.com/package/async-await-retry"target="_blank" rel="noopener">https://www.npmjs.com/package/async-await-retry</a></li>
<li><a href="https://stackoverflow.com/questions/11996988/how-good-is-oniguruma-compared-to-other-cross-platform-regexp-libraries"target="_blank" rel="noopener">https://stackoverflow.com/questions/11996988/how-good-is-oniguruma-compared-to-other-cross-platform-regexp-libraries</a></li>
<li><a href="https://www.baeldung.com/jni"target="_blank" rel="noopener">https://www.baeldung.com/jni</a></li>
<li><a href="https://www.elastic.co/blog/found-zookeeper-king-of-coordination"target="_blank" rel="noopener">https://www.elastic.co/blog/found-zookeeper-king-of-coordination</a></li>
<li><a href="https://github.com/akitaonrails/ObjC_Rubyfication"target="_blank" rel="noopener">https://github.com/akitaonrails/ObjC_Rubyfication</a></li>
<li><a href="https://zaiste.net/posts/shell-commands-rust/#"target="_blank" rel="noopener">https://zaiste.net/posts/shell-commands-rust/#</a></li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Hoje é dia de fazer um episódio que vinha postergando faz anos: falar sobre as principais linguagens de programação em uso no momento. Quem acompanha o canal sabe que evito ao máximo mostrar código dessa ou daquela linguagem, de vez em quando mostro uns trechos de javascript porque sei que a maioria sabe um pouco pelo menos, e em muitos casos uso pseudo-código que não existe quando o foco é o conceito.</p>
<p>Eu tenho algumas linguagens favoritas, obviamente Ruby, um pouco de Elixir, Crystal, todas linguagens meio parecidas em sintaxe, embora  diferentes por baixo, e mesmo assim  acho que nunca mostrei código delas em nenhum video. Mas hoje quero compartilhar algumas curiosidades técnicas e opiniões pessoais que muitos assistindo provavelmente nunca pararam pra pensar. A intenção não é fazer prós e contras, falar bem ou mau de qualquer linguagem. A idéia é fazer você enxergar sua linguagem favorita com outros olhos.</p>
<p>(&hellip;)</p>
<p>Já vou avisar: meu objetivo não é dizer que linguagem X é melhor que linguagem Y nem nada disso. Então os macacos de auditório de torcida organizada podem se retirar. Programação não é pra isso e código não liga pra sua &ldquo;lealdade&rdquo; a essa ou aquela tecnologia. Na verdade tecnologia e inovação recompensam a deslealdade. Quem conhece mais tecnologias diferentes sempre vai ser melhor e na verdade esse video é pra mostrar isso. Iniciantes vão ter seus níveis de ansiedade indo pras alturas porque vou mostrar um monte de coisas que nunca viram. Não se aflijam, anotem os termos que não conhecem, estudem, depois voltem pra assistir de novo.</p>
<p>A primeira coisa que quero levantar é o seguinte: Python. Nos últimos anos aumentou rápido de popularidade, principalmente por causa de projetos como Jupyter Notebooks, Pandas, Tensorflow, PyTorch e outros projetos relacionados a data science e machine learning. Se você se interessa por essas duas áreas, acho que é seguro dizer que maioria dos cursos e artigos a respeito vai usar uma ferramenta feita em Python. Por isso parece que Python é a linguagem que se deve aprender, certo?</p>
<p>Não está errado e não me entenda mau: apesar de não ser minha linguagem favorita, não tenho nenhum problema com Python. O que quero demonstrar hoje é mais dar uma perspectiva pé no chão. Primeiro de tudo, na minha opinião, Python estava no lugar certo na hora certa. Nos anos 90 a linguagem de script mais popular no nascente mundo de Linux foi Perl.</p>
<p>Acho que muitos iniciantes aqui nunca ouviram falar, mas muitos scripts de configuração e administração de sistemas Linux eram escritos em Perl. E quando a web começou a surgir, as primeiras aplicações interativas web, o que conhecemos hoje como programação back-end, era ou em C ou em Perl, porque essa linguagem era mais flexível pra lidar com strings, com um HTML, por causa da sua principal funcionalidade: Regular Expressions ou Regex.</p>
<p>Mais especificamente PCRE ou Perl Compatible Regular Expressions. Quem programa em Objective-C ou Swift pra iOS e Mac e já tentou usar regex, deve ter notado como é um pouco diferente do regex compatível com Perl. Isso porque a Apple, a Microsoft usam regex com sintaxe baseado em ICU que é International Components for Unicode. Eles são parecidos mas tem pequenas diferenças que podem confundir quem não sabe que existem dois padrões diferentes de regex.</p>
<p>Enfim, por causa de regular expressions, Perl sempre foi muito usado pra processar textos, logs, até coisas como sequências de DNA em pesquisa de genoma e coisas assim, já que envolve muito pattern matching de sequências. Porém, na virada do século, o pessoal de Perl quis dar passos maiores que as pernas. Eu lembro que queriam desenvolver um novo Perl, o Perl 6, como uma virtual machine que suportasse múltiplas sintaxes de linguagem por cima, o projeto Parrot.</p>
<p>Esse ciclo de desenvolvimento levou anos e nunca chegou no fim. O Perl 5, que foi a última grande versão de Perl ficou estagnado e sem novidades por muito tempo. Enquanto isso outras linguagens evoluíram mais rápido e de forma mais pragmática, e uma delas foi o Python. Não sei se foi a RedHat quem adotou primeiro, mas o fato é que com a incerteza no futuro do Perl, acredito que muitos começaram a migrar pra Python.</p>
<p>Python também tinha uma sintaxe e ergonomia muito mais modernas. Perl não era originalmente orientado a objetos, embora tivesse como gambiarrar classes com <code>bless</code>. Era uma linguagem dinâmica de tipagem fraca, então bugs por causa de coerção de valores e tipos errados não era incomum. Mas Perl influenciou todas as linguagens de script que vieram depois, incluindo Python, Ruby e o mais óbvio, PHP. De onde vocês acham que variáveis com cifrão vieram? De Perl.</p>
<p>O principal legado de Perl, na minha opinião, foi popularizar a viabilidade de linguagens dinâmicas pra aplicações web e popularizar o uso de regular expressions pra pattern matching de textos. Várias linguagens até hoje usam a sintaxe específica de Perl, como em Ruby, Node.js, acho que Go também porque usa a biblioteca &ldquo;re2&rdquo; que é compatível com PCRE.</p>
<p>Na realidade, nem todas são 100% compatível com Perl. Por exemplo, em Python, se usar a biblioteca &ldquo;re&rdquo;, o método <code>match</code> tem comportamento como se toda regex começasse com crase, que em Perl significa dizer pra começar o matching do começo do string. Ele também não tem o operador global &ldquo;/g&rdquo;, pra isso se usa o método &ldquo;re.sub()&rdquo;.  São pequenas diferenças, mas pode confundir quem conhecia regex de Perl, por exemplo, se costuma usar Vim como editor, que suporta regex Perl.</p>
<p>De qualquer forma, linguagens que quase totalmente suportam a sintaxe de regex de Perl são Python, Javascript, PHP, Ruby. Como falei antes, a biblioteca &ldquo;re&rdquo; de Python é só uma casca pra biblioteca Oniguruma, que em japonês significa carro do demônio, sei lá porque. A gem Oniguruma de Ruby é a mesma coisa. A classe RegExp de Javascript também. Oniguruma é uma das melhores e mais rápidas bibliotecas de regex que existem. Mas não é a única. Linguagens como C#, C++, Go usam outra biblioteca, a &ldquo;RE2&rdquo;. Não confundir com Resident Evil 2.</p>
<p>Acho que nem a Oniguruma e nem a RE2 forçam a sintaxe de Perl. Eles oferecem o motor, mas os detalhes da sintaxe, cada linguagem pode configurar como quiser. Por exemplo, C# tem a opção de usar &ldquo;verbatim string literal&rdquo; que é escrever a regex com arroba na frente, assim não precisa escapar caracteres especiais dentro do regex com barra ao contrário.</p>
<p>Algumas linguagens não usam o estilo Perl e sim a sintaxe ICU, de International Components for Unicode. Exemplos disso são a classe <code>java.util.regex</code> de Java, a classe <code>icu::RegexPattern</code> de C++, a classe <code>NSRegularExpression</code> de Swift da Apple, as funções &ldquo;preg_*&rdquo; de PHP. Eu pessoalmente gosto mais da sintaxe Perl. Quando precisava usar ICU pra programar mobile de iOS em Swift ou Objective-C, lembro que vira e mexe tropeçava na sintaxe.</p>
<p>11 anos atrás fiz um pequeno experimento em Objective-C pra importar o Oniguruma e adicionar sintaxe de regex de Perl pra programar iOS. É uma das poucas coisas que ainda sobrou no meu GitHub. Maioria dos meus códigos sempre foi pra projetos de cliente por isso tenho bem pouca coisa aberta. Essa foi uma delas. 11 anos atrás foi 2011, pouco mais de 2 anos depois que a App Store abriu oficialmente.</p>
<p>Além do regex não ser compatível com Perl, formatação de data e hora também não era compatível com a biblioteca Time de Linux. Sabe a função strftime? Onde podemos pedir pra formar data e hora usando uma string de template como &ldquo;%d/%m/%Y %H:%M:%S&rdquo;?</p>
<p>Em Objective-C e depois Swift, usando a classe <code>DateFormatter</code> com o método <code>dateFormat</code>, o mesmo template fica assim: &ldquo;dd/MM/yyyy HH:mm:ss&rdquo;. É bem diferente e isso me incomodava porque Ruby usa o mesmo padrão de C mas de novo, Objetive-C e Swift usam o padrão ICU.</p>
<p>Diferente de Regex onde a sintaxe Perl ainda é forte em muitas linguagens em vez do formato ICU, no caso de formatação de data e hora, o formato ICU é mais forte do que strftime de C. C++, Python, Ruby, Elixir ainda usam o formato de C, mas Swift, Java, C#, até PHP e JavaScript usam o formato ICU. Tecnicamente, por serem padrões internacionais de Unicode, o formato ICU é quem deveria ser considerado o &ldquo;correto&rdquo;, mas por direito, formatos derivados do mundo Unix e Linux continuam sendo usados.</p>
<p>E de novo, naquele projetinho de biblioteca que fiz pra Objective-C, além de importar Oniguruma, também implementei uma versão de formatador de data e hora usando a função &ldquo;strftime&rdquo; da biblioteca &ldquo;time&rdquo; de C. Assim eu poderia usar os formatos de Linux e contrariar o formato ICU. E por que? Só pra mostrar que era possível.</p>
<p>E eu contei essas duas histórias pra ilustrar um ponto. Não sei se vocês entendem que muita coisa de Javascript ou Python não são escritos em Javascript ou Python e sim em C. A classe RegExp de Javascript ou &ldquo;re&rdquo; de Python são meras casquinhas mas por baixo chamam bibliotecas escritas em C.</p>
<p>Eu digo isso porque muitas linguagens são escritas nelas mesmas. O compilador de Rust é escrito em Rust. O compilador de Go é escrito em Go. Muito do OpenJDK de Java é escrito em Java. Linguagens compiladas costumam muitas vezes ser escritos nelas mesmas. Isso não torna automaticamente uma linguagem melhor ou pior. Apenas facilita pros desenvolvedores das linguagens não ter que ficar mudando de linguagem o tempo todo. Por exemplo, escrever o compilador em C mas as bibliotecas em Rust. Lembrei disso quando conversei com os criadores e mantenedores de Ruby e eles comentando que escrevem mais C que Ruby.</p>
<p>Isso é mais uma curiosidade, não comecem a brigar em Reddit dizendo que sua linguagem é melhor porque é bootstrapped, ou seja, escrita nela mesma. Mais importante é o seguinte: linguagens interpretadas de script como Python, Ruby, Javascript ou PHP herdam muitas características da plataforma onde nasceram: do C. Diferente de Java ou Go que é na maior parte escrita nelas mesmas, linguagens de scripts costumam parecer mais &ldquo;cascas&rdquo; pras funcionalidades de C.</p>
<p>Veja a função de formatação de data e hora &ldquo;strftime&rdquo; de Ruby ou &ldquo;datetime.strftime&rdquo; de Python. Por que acha que tem esses nomes? Porque herdaram da função strftime da biblioteca time, cujo cabeçalho se chama &ldquo;time.h&rdquo;. Se num Linux, abrir um terminal e digitar &ldquo;man strftime&rdquo; vai dar os detalhes da função original de C. Essa função fica disponível numa biblioteca compartilhada chamada &ldquo;libc&rdquo; ou &ldquo;glibc&rdquo;, que praticamente todo programa que roda em Linux carrega e usa.</p>
<p>Vamos ver mais exemplos? Em Python, claro, muitos dos métodos do módulo &ldquo;os&rdquo; são derivados da libc. Temos &ldquo;os.mkdir()&rdquo; pra criar diretórios, não por acaso, é o mesmo nome do comando que você usa em qualquer terminal de linha de comando pra criar diretórios, porque ambos usam a mesma função da libC. Ou &ldquo;os.getenv()&rdquo; que é o mesmo nome da função da libC pra pegar variáveis de ambiente. Tem funções do módulo &ldquo;ctypes&rdquo; que, como o nome diz são &ldquo;tipos de C&rdquo;, como &ldquo;ctypes.memcmp()&rdquo; que compara dois blocos de memória e retorna um inteiro. Em particular vários métodos do módulo &ldquo;math&rdquo; herdam funções do cabeçalho &ldquo;math.h&rdquo; da libC como &ldquo;math.sin()&rdquo; pra calcular seno ou &ldquo;math.pow()&rdquo; pra calcular a potência de um número.</p>
<p>A mesma coisa vale pra Javascript, Ruby, PHP. E eu quero pegar esse gancho pra voltar pros projetos mais populares de Python. Vamos pegar o Pandas que é uma ferramenta muito poderosa, flexível e razoavelmente amigável de usar pra fazer análise e manipulação de dados. Povo de data science acho que começa aprendendo Pandas. Se abrirmos a página do projeto no GitHub, podemos ver que a maior parte é escrita em Python mesmo.</p>
<p>Mas essa não é a história toda. Como todo bom projeto moderno de Python, tem um arquivo de &ldquo;requirements&rdquo; que lista as dependências que o projeto precisa carregar pra funcionar, vamos dar uma fuçada. Tem uma quantidade enorme de bibliotecas, mas 2 das principais incluem o numpy e scipy. Vamos começar pelo numpy que é importantíssimo porque fornece coisas como array multidimensional, funções de álgebra linear, transformada de Fourier e mais.</p>
<p>Vamos abrir o projeto no GitHub e olha só: um terço é código C. Um dos pontos fortes do numpy é justamente facilitar integrar com bibliotecas em C e Fortran. É por isso que pra gerar o pacote pra Mac, por exemplo, o script que tá na wiki manda instalar o pacote de Fortran. Ou na página de wiki pra Windows, precisa do MSVC que é o pacote de compiladores de C e C++ do Visual Studio da Microsoft. Sim, pra gerar o pacote de um projeto de Python, precisa de compiladores de C, C++ e Fortran. Sabiam disso?</p>
<p>O numpy é requisito pra usar o scipy que é um conjunto de módulos científicos pra estatística, otimização, integração, álgebra linear, processamento de sinais e imagens e muito mais. Ele usa os arrays multidimensionais de numpy por exemplo. E no que SciPy é feito? Quase um quinto do código é Fortran e quase outro quinto é C e C++. Ou seja, pelo menos dois quintos do projeto todo também não é em Python e também precisa de compiladores de Fortran, C e C++.</p>
<p>Todo projeto de data science feito em Python precisa usar numpy e scipy, que são os grandes diferenciais, e eles são feitos em boa parte em Fortran, C e C++ e não em Python. E isso não é pra minimizar, denegrir, ou falar mal de Python, mas sim pra quem não sabia entender que só porque a cara da frente é numa linguagem, não significa que por baixo do capô também é.</p>
<p>E o Tensorflow que todo mundo fica hypado pra machine learning? Vamos ver a página no GitHub? Olha só, bem mais da metade do código é em C++, só um quinto dele é Python. O grosso, o motor mesmo, é todo em C++. E o PyTorch que é redes neurais acelerado por GPU? Metade desse código é em C++. E isso porque eu não estou olhando as dependências no arquivo de requirements.</p>
<p>Uma parte significativa que não tá contando aqui é a biblioteca de CUDA da NVIDIA e os shaders pra aceleração em GPU, que podem ser escritos em várias linguagens, incluindo Python mas compilam pra HLSL ou GLSL que é o OpenGL Shader Language. Eu falo sobre linguagens de shaders e GPU no video da longa história de CPUs e GPUs, depois dêem uma olhada.</p>
<p>Pra usar esses projetos, sabendo um pouco de Python é suficiente, mas se alguém queria realmente entender como redes neurais,  aceleração via GPU pra esse tipo de computação realmente funciona, obrigatoriamente não é Python e sim C++ e linguagens de shaders pra GPU. Eu estou sendo pedante nisso porque da forma como muita gente faz propaganda, parece que é tudo feito em Python e basta saber Python pra resolver tudo nessa área, o que não é verdade.</p>
<p>E de novo, meu objetivo não é pegar no pé do Python. Como contei no começo, ele estava no lugar certo na hora certa quanto Perl 5 foi desaparecendo. Linguagens de script como Perl ou Python, que foram feitos pra funcionar muito perto do sistema operacional, são ótimos pra funcionar como cascas pras bibliotecas de alta performance feitos em C ou C++.</p>
<p>Era esse tipo de coisa que se fazia com  Perl, que era menos ruim do que escrever scripts de SH ou Bash, e depois passamos a usar Python que, com sua sintaxe bem simples e espartana, caiu como uma luva pra administradores de sistemas que precisavam fazer coisas rápidas.</p>
<p>Com o tempo passou a ser adotado em distros com programas como o Conda que é escrito em Python e serve pra instalar e gerenciar pacotes. Não confundir com Anaconda que é outro projeto pra computação científica. Daí o pessoal de pesquisa do Google passa anos desenvolvendo um Tensorflow da vida em C++. Nesse estágio ele é difícil de usar, precisa saber C++. Em cima dele fizeram scripts em Python e um ferramental com mais usabilidade e mais simples de usar pra maioria das pessoas. Essa é vantagem do Python.</p>
<p>Diferente do Tensorflow de Python, o tfjs ou Tensorflow.js é na maior parte escrito realmente em TypeScript e Javacript. Assim como em Python as partes que exigem performance e falar diretamente com o hardware como GPUs, são feitos em C++, mas na versão de Javascript ele pode rodar no navegador e utiliza o suporte a WebGL e Web Assembly. A versão de Javascript não faz tudo que a de Python mas é suficiente. Tem o Tensorflow Lite pra rodar em smartphones também. Enfim, o principal é entender que quando precisamos de performance normalmente precisamos mais do que Python.</p>
<p>E pra não pegar só no pé do Python, Javascript é similar. Tudo que é impressionante e roda rápido no navegador não é mérito do Javascript e sim do navegador que oferece APIs como WebGL pra dar acesso à aceleração via GPU. E todo navegador é feito em C++. Entendam, toda linguagem interpretada depende muito do sistema operacional e outras bibliotecas instaladas.</p>
<p>Voltando ao ponto onde linguagens como Python, Javascript ou PHP dependem de bibliotecas como a LibC, - tanto que os nomes das funções são as mesmas das versões em C -, e se a LibC costuma vir em toda distro Linux mas não em Windows ou Mac, como que faz pra rodar Python em Windows? Vamos pro GitHub do CPython e vou te mostrar o que tem embaixo do tapete. Se procurarmos dentro desse repositório por &ldquo;ifdef MS_WINDOWS&rdquo; temos 8 páginas de resultados e olhem esses trechos.</p>
<p>Se nunca programou em C e não sabe o que é pré-processamento, deixa eu pegar um arquivo aleatório desse resultado. Vamos abrir o arquivo &ldquo;_ssl.c&rdquo;. Olha aqui na linha 628 tem um &ldquo;ifdef MS_WINDOWS&rdquo;. Se não existisse pré-processamento seria como se tivéssemos duas versões desse arquivo. Um chamado &ldquo;_ssl.c&rdquo; que teria esse trecho assim e outro arquivo chamado &ldquo;_ssl.c-ms_windows&rdquo; cujo trecho seria escrito dessa outra forma aqui. Conseguem ver a diferença?</p>
<p>O compilador, se estiver em Windows, vai compilar o código com esse &ldquo;if&rdquo; a mais chamando essa função &ldquo;SetFromWindowErr&rdquo; que só precisa no Windows. Em Linux ou Mac esse código não existe. E isso acontece em dezenas de arquivos, 8 páginas de resultados no GitHub. Ocódigo do CPython pra Windows é diferente da versão de Linux.</p>
<p>Por isso o CPython de Windows não é igual ao CPython de Linux que não é igual ao CPython de Mac. Isso vale pra tudo PHP de Windows, Javascript de Windows. Então vamos falar de Node.js. O Node é basicamente um projeto de C++. Um quinto desse projeto é só C++ mas precisamos falar de uma das dependências, o libuv. O coração da principal funcionalidade do Node, que é o famoso eventloop, o reactor, é implementado quase 100% em C. Vamos abrir a página dele no GitHub. Olha só aqui embaixo, 97% de C, mas o resto é só coisas administrativa, por isso é 100%.</p>
<p>E o que é a libuv. Novamente, sendo uma biblioteca de C, num terminal de Linux podemos usar o comando &ldquo;man&rdquo; que, se alguém tinha dúvida, significa &ldquo;manual&rdquo; e não &ldquo;homem&rdquo;. Normalmente bibliotecas de C instalam sua documentação nesse formato. E como diz aqui, esse projeto foi feito pelos desenvolvedores do Node.js pra ser uma biblioteca com capacidade pra rodar em diversas plataformas como Windows, Mac, BSD e é quem abstrai as funcionalidades de I/O assíncrono que o Node precisa pro seu reactor.</p>
<p>Cada sistema operacional implementa I/O assíncrono diferente. Linux costuma ter suporte a epoll, UNIX BSD tem Kqueue, Windows tem IOCP que é I/O Completion Ports, que muitos argumentam que é superior a epoll ou kqueue. Ou seja, a principal funcionalidade do Node só é possível porque quem de fato fornece o músculo é o sistema operacional. O libuv é mais uma casca em C++ pra abstrair essas diferentes formas de fazer I/O assíncrono e tornar possível o Node rodar em qualquer sistema operacional.</p>
<p>E o Deno, o competidor que muitos consideram melhor que o Node? Eu nem quero tentar entrar nessa discussão de quem é melhor. Mas se abrirmos a página GitHub do Deno, vemos que mais da metade, mais de 60% é escrito em Rust. Ahhh será que é por isso que ele é melhor que o Node então? Na verdade não. Se procurarmos por libuv nesse repositório, vemos que o Rust importa as mesmas funções do libuv do Node, então em termos do eventloop, ambos fazem a mesma coisa. Claro que podem parametrizar pra se comportar diferente, mas a fundação é a mesma e, de novo, é C++.</p>
<p>Eu poderia passar o dia todo dando exemplos em Ruby, em PHP, e até mesmo em Java. Mas ué, tudo que é de Java não era escrito todo em Java? Na realidade não. Como último exemplo, vamos abrir a página do OpenJDK e olha aqui embaixo, 13% do Java é escrito em C++. Mas o que? Vamos ver, olha só, muita coisa do HotSpot que é o motor do JIT ou Just in Time Compiler da JVM é escrito em C++.  Nem mesmo o Java, que nasceu nos anos 90 com a frase de &ldquo;Write Once, Run Anywhere&rdquo; é puramente feito em Java. E se isso não ficou claro, olha a página de resultados de arquivos C++: tem pelo menos 100 páginas de resultados, uma parte considerável da JVM não é feito em Java.</p>
<p>E isso não é perseguição. Ruby, PHP, DotNet, Go, Rust, Swift, e todo o resto tem partes consideráveis que depende de C e C++, coisas como a LibC, coisas de criptografia como OpenSSL ou alternativas como libsodium. Imagina se toda linguagem tivesse que fazer criptografia do zero? O mundo já tem bugs de segurança demais. É melhor todo mundo reusar o que já existe de melhor e mais maduro.</p>
<p>E isso só falando em Linguagens. Quem fez Ciências da Computação decentemente não perde muito tempo com discussões de melhor linguagem. Mas pessoal de desenvolvimento web, por alguma razão vira e mexe entra nesse tipo de argumento. Então só pra complementar, além de linguagens, no mundo web temos diversos outros serviços e componentes que precisamos usar no nosso dia a dia.</p>
<p>Por exemplo, no que é escrito o banco de dados Postgres? É mais de 85% de C. E o MySQL, mais especificamente o MariaDB? Metade é C++ e quase 40% é C. Bom, mas isso deve ser porque são velhos né, um banco de dados mais novo como MongoDB deve ser em outra coisa mais moderna? Não, 72% é C++.</p>
<p>E o Redis que usamos pra cache ou filas o tempo todo? É quase 80% de C. Mas não significa que tudo é em C ou C++. Vejamos o banco de dados distribuído Cassandra, esse é quase 100% feito em Java e não é o único. O Apache Kafka que é o sistema de filas que falei no episódio passado, é quase 75% em Java e 22% em Scala, que é outra linguagem que roda na JVM então 97% é Java. E falando em sistemas de fila, outro com excelente reputação e maturidade e que, se não me engano, era uma das estrelas por trás de serviços como Whatsapp, é o RabbitMQ que é escrito em Erlang, a plataforma por trás da moderna linguagem Elixir.</p>
<p>E aqui vem outra coisa que eu queria explicar. Tanto Java quanto Erlang são muito bons pra construir sistemas distribuídos, de forma mais produtiva do que fazer em C ou C++. Go também serviria, mas ele ainda é muito novo comparado com os outros, talvez o próximo grande banco de dados seja em Go, não sei. Ele ainda não se provou nesse espaço. Então deixa eu dar minha perspectiva sobre onde cada linguagem tem mais força.</p>
<p>Todo sistema operacional hoje é feito numa mistura de C e C++. E quando eu falo sistema operacional ou OS quero dizer coisas como a kernel e drivers de dispositivos, os componente que rodam no Ring Zero de maior privilégio. Todo o resto, nossos programas e scripts rodam no Ring 3, que tem menos privilégios, o que chamamos de userland. Todo programa em userland precisa usar o kernel do OS como intermediador pra falar com dispositivos como SSD, placa de rede, placa de video, teclado, mouse e tudo mais. O único que tem permissão pra falar com o hardware diretamente costuma ser o kernel e seus drivers, que rodam no mesmo espaço de memória.</p>
<p>Isso não vai mudar por muito tempo. A kernel XNU da plataforma Darwin do Mac, a kernel NT do Windows, a kernel do BSD UNIX, que eu não lembro se tinha codinome. Dispositivos Android usam a kernel do Linux. Dispositivos iOS como iPhone usam uma kernel derivada do MacOS. Existem outras de nicho, mas essas são as principais e vão continuar sendo por muito tempo. E todas elas tem uma coisa em comum: são escritos em C e C++. E porque isso importa já que no final, depois de compilar, vira tudo um binário?</p>
<p>Porque essas kernels expõe suas funcionalidades como funções de C, que podemos interfacear com os diversos arquivos de cabeçalho com extensão &ldquo;.h&rdquo;. Pra realmente entender isso, precisa entender um pouquinho de como compilação de C funciona. Eu expliquei parte disso em episódios como do Hello World, a diferença de arquivos texto e binário e o episódio de linguagem interpretada versus compilada. Depois assistam pra entender.</p>
<p>A explicação que vou fazer agora acho é um pouco errado, mas é pra simplificar o modelo na cabeça. Se alguém tiver uma explicação de forma mais simples, mandem nos comentários abaixo. Mas pense num driver, como driver da NVIDIA pra placa de video, ou driver pra placa de rede e coisas assim com uma DLL de Windows ou uma biblioteca .SO de Linux. A kernel depois que boota começa a carregar esses drivers. Pense um &ldquo;import&rdquo; em Python ou Javascript.</p>
<p>As estruturas de dados e funções que esses drivers implementam vão existir no mesmo espaço de memória que as outras estruturas da própria kernel. É como se a kernel já viesse embutida com esses drivers. Uma vez carregado, o driver enxerga tudo que a kernel enxerga e vice versa. Lembra ponteiros? A kernel pode manipular diretamente uma struct de uma placa de rede, por exemplo.</p>
<p>Isso é necessário porque no nível da kernel gostaríamos de ter a maior performance possível, o menor número de abstrações. A kernel ideal não desperdiça recursos fazendo indireções e abstrações. Por causa disso você nunca vai conseguir fazer um driver em Javascript, Python, Java ou Erlang.</p>
<p>O interpretador de Javascript, como o Google V8 ou o Spidermonkey da Mozilla, assim como a JVM de Java ou a BEAM de Erlang, poderíamos classificar mais ou menos como máquinas virtuais. A intenção delas é abstrair o sistema operacional por baixo, a kernel. Assim um programador de Python pode escrever praticamente o mesmo código pra rodar em Mac ou Windows ou Linux e o interpretador do Python vai se virar pra fazer funcionar embaixo. A JVM é a que mais esconde o OS por baixo, Python ou Javascript escondem menos e às vezes precisa fazer um &ldquo;if windows faça X else if Linux faça Y&rdquo; mas é raro precisar.</p>
<p>Pra ser capaz de criar um driver de dispositivo que consiga interagir direto com a kernel, em particular carregar no mesmo espaço de memória, precisa ser uma linguagem compilada que gera binários com a mesma ABI de C, ou seja cujo binário seja compatível com binários gerados pelo compilador de C em termos de tipos de dados básicos terem o mesmo tamanho, mesmo alinhamento, mesmo layout de structs e unions e chamadas de funções sigam as mesmas convenções, tipo uma compatibilidade de API no nível binário.</p>
<p>Outra coisa que talvez você não saiba de linguagens compiladas como C, C++, Rust, Nim, D ou Go e linguagens tecnicamente interpretadas como Python, Ruby, Javascript, PHP, e até Java, C# ou Erlang e Elixir. Eu digo &ldquo;tecnicamente interpretadas&rdquo; porque Java não gera binário nativo. Sim, Java ou Javascript tem coisas como JIT na JVM ou V8, que no final pode gerar binário de alguns trechos, mas originalmente não são binários nativos. Só em tempo de execução.</p>
<p>O problema de não ter binário nativo de antemão é que não sabemos o endereço de funções e estruturas de dados. Por isso não temos como fazer um código de C que aponta diretamente pra essas estruturas usando coisas como ponteiros. Mesmo assim é possível integrar binário feito em Rust ou C com programas em Java ou Javascript. Por exemplo, um JavaScript consegue importar bibliotecas como OpenSSL, só que um String de Javascript não é um tipo de dados igual a um String de C. Um String de Python não é igual a um String de C#, então como faz pra uma linguagem falar com outra?</p>
<p>A única forma do binário gerado por uma linguagem conseguir usar um tipo de dados diretamente via ponteiro de outra linguagem, só é possível se são binariamente iguais, tenham a mesma ABI. Quando não tem, precisa existir um processo de conversão. Você já deve ter ouvido falar dos termos serializar e desserializar ou marshalling e unmarshalling. A grosso modo, no mundo de web seria mais ou menos como converter uma estrutura XML num JSON ou vice versa.</p>
<p>O que acontece quando se chama uma API que só devolve XML mas precisa passar pra uma classe ou outra biblioteca que só entende JSON? Precisa converter, só que conversão tem o seguinte defeito. Veja este exemplo de XML que a API hipotética devolveria, ela tem  845 bytes.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;product&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;name&gt;</span>Playstation 5<span class="nt">&lt;/name&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;sku&gt;</span>PS5<span class="nt">&lt;/sku&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;manufacturer&gt;</span>Sony<span class="nt">&lt;/manufacturer&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;category&gt;</span>Video Games<span class="nt">&lt;/category&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;description&gt;</span>
</span></span><span class="line"><span class="cl">    The Playstation 5 is the next-generation gaming console from Sony, featuring lightning-fast loading, stunning graphics, and immersive gameplay.
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/description&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;specifications&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;CPU&#34;</span><span class="nt">&gt;</span>AMD Zen 2<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;GPU&#34;</span><span class="nt">&gt;</span>AMD RDNA 2<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;Memory&#34;</span><span class="nt">&gt;</span>16 GB GDDR6<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;Storage&#34;</span><span class="nt">&gt;</span>1 TB NVMe SSD<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;Dimensions&#34;</span><span class="nt">&gt;</span>39 x 10 x 26 cm<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;specification</span> <span class="na">name=</span><span class="s">&#34;Weight&#34;</span><span class="nt">&gt;</span>4.5 kg<span class="nt">&lt;/specification&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/specifications&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;pricing&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;price</span> <span class="na">currency=</span><span class="s">&#34;USD&#34;</span><span class="nt">&gt;</span>499.99<span class="nt">&lt;/price&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/pricing&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;inventory&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;quantity&gt;</span>50<span class="nt">&lt;/quantity&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/inventory&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/product&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Digamos que fizemos um código pra converter de XML pra JSON ou seja, faz marshall entre os dois formatos, e eis o JSON equivalente:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;product&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Playstation 5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sku&#34;</span><span class="p">:</span> <span class="s2">&#34;PS5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manufacturer&#34;</span><span class="p">:</span> <span class="s2">&#34;Sony&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;category&#34;</span><span class="p">:</span> <span class="s2">&#34;Video Games&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;The Playstation 5 is the next-generation gaming console from Sony, featuring lightning-fast loading, stunning graphics, and immersive gameplay.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;specifications&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;specification&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;CPU&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;AMD Zen 2&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;GPU&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;AMD RDNA 2&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Memory&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;16 GB GDDR6&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Storage&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;1 TB NVMe SSD&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Dimensions&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;39 x 10 x 26 cm&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Weight&#34;</span><span class="p">,</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;4.5 kg&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pricing&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;price&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;currency&#34;</span><span class="p">:</span> <span class="s2">&#34;USD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mf">499.99</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;inventoryQuantity&#34;</span><span class="p">:</span> <span class="mi">50</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse JSON é um pouco menor, com 808 bytes. O importante é o seguinte: nesse instante a função que converte tem em memória as duas versões, ocupando o total de 845 + 808 que dá mais de 1600 bytes. Agora pense no nível de uma struct de C versus um objeto JSON de Javascript. Não tem como criar um ponteiro pra um objeto JSON a partir de um código C e tentar usar como se fosse uma struct. Se fossem compatíveis, não precisaria duplicar memória, mas como não são compatíveis, precisamos fazer marshalling, duplicando a memória usada.</p>
<p>Eu acho que muito iniciante não pensa assim, mas comece a enxergar onde memória é duplicada. Por exemplo, quando você abre seu navegador e pede uma página web pra amazon.com por exemplo, sabemos que o servidor web da Amazon vai processar o que precisar pra no final gerar um string gigante que é o conteúdo HTML pra te responder. Esse HTML está ocupando memória no servidor. Quando ele envia pra você, e chega no seu navegador, o navegador tem uma cópia dessa string. Tanto o servidor web quanto seu navegador tem uma cópia do mesmo HTML, duplicando memória nos dois lugares. Isso é inevitável.</p>
<p>O mesmo vale pra um script feito em Javascript ou Python. Lembra da tal função strftime pra formar data e hora que falei lá no começo do episódio? Por baixo é uma função em C. No Python criamos a String &ldquo;%d/%m/%Y&rdquo; pra formatar a data e chamamos &ldquo;datetime.strftime&rdquo;. Por baixo essa função vai chamar a função da LibC que também chama &ldquo;strftime&rdquo;, pra isso precisa criar uma String de C, mas a String de Python não é compatível com String de C, então precisa converter e isso vai duplicar esse String.</p>
<p>Esse é um dos custos escondidos que tem em várias linguagens quando integram com bibliotecas locais feitas em C por exemplo, ou quando fazemos chamadas remotas pra APIs de microserviços ou servidores de banco de dados. Várias estruturas de dados como Strings estão o tempo todo sendo duplicados pra converter num formato que o outro lado entende. O exemplo que dei de XML e JSON são pequenos, nem 1 kilobyte, mas no mundo real, quem já trabalhou em projetos de verdade sabe que estamos transitando documentos muito maiores que isso, de centenas de usuários o tempo todo. É uma das razões de tanta diferença no uso de recursos entre diferentes linguagens ou diferentes bibliotecas.</p>
<p>Quando Java tinha a noção de escrever tudo em Java, ou seja, tudo rodando dentro do mesmo processo da mesma JVM, onde todas as bibliotecas também seriam em Java, não era só pra ser bonito. Uma das vantagens é que se tudo fosse feito em Java, internamente não precisa ter marshalling e unmarshalling, poderia lidar diretamente com referências pras estruturas de dados. Sendo que se integrar como uma biblioteca feita em C usando o recurso de JNI ou Java Native Interface,  precisa existir uma tradução entre o layout do espaço de memória de Java pro espaço do C. O mesmo vale pra outras linguagens que não trabalham com os mesmos tipos de C.</p>
<p>Se ficou abstrato até aqui, deixa eu mostrar um exemplo super besta em Python. Considere o seguinte trecho de código em Python:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Import the extension module</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">c_extension</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Call the py_c_function function with a Python string argument</span>
</span></span><span class="line"><span class="cl"><span class="n">c_extension</span><span class="o">.</span><span class="n">py_c_function</span><span class="p">(</span><span class="s2">&#34;Hello, C function!&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Aqui eu estou importando um módulo qualquer feito em C e em seguida chamo uma função declarada nesse módulo passando um String qualquer. Vamos ver como teríamos que fazer essa função em C.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;Python.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Declare a C function that expects a char* as a parameter
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">c_function</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Do something with the C string here
</span></span></span><span class="line"><span class="cl">  <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;C function received string: %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Define a Python wrapper function for the C function
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">py_c_function</span><span class="p">(</span><span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Extract the Python string argument from the Python tuple
</span></span></span><span class="line"><span class="cl">  <span class="n">PyObject</span><span class="o">*</span> <span class="n">py_s</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="s">&#34;O&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">py_s</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Convert the Python string to a C string
</span></span></span><span class="line"><span class="cl">  <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">s</span> <span class="o">=</span> <span class="nf">PyBytes_AsString</span><span class="p">(</span><span class="n">py_s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Call the C function with the C string
</span></span></span><span class="line"><span class="cl">  <span class="nf">c_function</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Return None to indicate that the function succeeded
</span></span></span><span class="line"><span class="cl">  <span class="n">Py_RETURN_NONE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Primeiro declaro a tal função <code>c_function</code> que recebe uma string e imprime na tela. Um string em C é só um array de caracteres que termina com caracter nulo. Já uma string em Python é um objeto, uma estrutura de dados bem diferente. Por isso embaixo declaramos uma segunda função chamada <code>py_c_function</code> que recebe um objeto Python cujo tipo é um ponteiro pra <code>PyObject</code> chamado <code>args</code>. O primeiro parâmetro é o <code>self</code> que todo método de Python declara explicitamente, o equivalente <code>this</code> em Javascript, por exemplo.</p>
<p>O importante nessa função é essa <code>PyBytes_AsString</code> que vai devolver o ponteiro pro array de chars que está nessa variável <code>py_s</code>. Agora, praquele código em Python conseguir chamar esse código em C, precisamos de outro código que vai intermediar isso, escrito em Cython com extensão &ldquo;.pyx&rdquo; mais ou menos assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># c_extension.pyx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Declare a C function that expects a char* as a parameter</span>
</span></span><span class="line"><span class="cl"><span class="n">cdef</span> <span class="n">void</span> <span class="n">c_function</span><span class="p">(</span><span class="n">const</span> <span class="n">char</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="n">nogil</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Do something with the C string here</span>
</span></span><span class="line"><span class="cl">    <span class="n">printf</span><span class="p">(</span><span class="s2">&#34;C function received string: </span><span class="si">%s</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Define a Python wrapper function for the C function</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">py_c_function</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Convert the Python string to a C string</span>
</span></span><span class="line"><span class="cl">    <span class="n">cdef</span> <span class="n">const</span> <span class="n">char</span><span class="o">*</span> <span class="n">cs</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Call the C function with the C string</span>
</span></span><span class="line"><span class="cl">    <span class="n">c_function</span><span class="p">(</span><span class="n">cs</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse é o código que faz o tal marshalling, ou conversão, do objeto de String do Python pra um array de char, é isso que faz o método &ldquo;encode&rdquo; da String, duplica a string. Isso é importante porque o objeto de String de Python está sob controle do garbage collector de Python. Digamos que ele decida que ninguém tá usando mais essa String e limpa da memória. Mas no lado do C, que o garbage collector não enxerga, a String ainda era necessária, daí iria crashear o programa. Mas passando uma cópia separada, agora cada um tem seu próprio string e um não pisa no pé do outro, mas o custo disso foi duplicar essa string.</p>
<p>É assim que linguagens como Python, Javascript, Ruby ou PHP fazem pra falar com funções de bibliotecas em C: via marshalling e unmarshalling de estruturas de dados, onde cada lado gerencia a memória do seu jeito. No lado do Python o garbage collector dele se vira com os objetos que ele criou. E do lado do C vai usar, por exemplo, malloc e free, pra lidar com seu espaço de memória. Por isso que precisa duplicar memória na comunicação entre eles e por isso é mais pesado.</p>
<p>E qual a diferença com uma linguagem que compila binário nativo compatível com C como Rust? Vamos fazer a mesma coisa usando Rust agora. Eis um exemplo de um código Rust que chama uma função de alguma biblioteca C qualquer que recebe uma String como parâmetro, e sendo em C, sabemos que String de C é sempre um ponteiro pra um array de chars terminado em nulo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">ffi</span>::<span class="n">CString</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">os</span>::<span class="n">raw</span>::<span class="n">c_char</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Declare the C function using the extern keyword
</span></span></span><span class="line"><span class="cl"><span class="k">extern</span><span class="w"> </span><span class="s">&#34;C&#34;</span><span class="w"> </span><span class="k">fn</span> <span class="nf">c_function</span><span class="p">(</span><span class="n">s</span>: <span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="n">c_char</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Do something with the C string here
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;C function received string: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">CStr</span>::<span class="n">from_ptr</span><span class="p">(</span><span class="n">s</span><span class="p">).</span><span class="n">to_str</span><span class="p">().</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Create a Rust string
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Hello, C function!&#34;</span><span class="p">.</span><span class="n">to_owned</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Convert the Rust string to a CString
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">c_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="n">s</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Call the C function with the CString
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">c_function</span><span class="p">(</span><span class="n">c_string</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">())</span><span class="w"> </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse &ldquo;extern C&rdquo; é pra declarar a assinatura da função C externa que vamos chamar, cujo nome é <code>c_function</code> de novo e o parâmetro é o ponteiro pro array de char. Na função <code>main</code> começamos criando um String de Rust. Só que, assim como no caso do Python, um String de Rust é um objeto, uma estrutura de dados diferente de array de char, por isso criamos uma <code>CString</code> que existe no módulo &ldquo;std::ffi&rdquo; pra converter o String de Rust num String de C. Finalmente, dentro de um bloco inseguro, <code>unsafe</code> chamamos a função de C passando esse novo String de C como ponteiro.</p>
<p>Assim como em Python, tivemos que duplicar a memória pra converter os strings. Mas em Rust podemos pular a parte de criar a string de Rust e direto já trabalhar com String de C. Pra demonstrar isso deixa eu começar fazendo uma nova função em Rust besta que recebe um String e converte tudo pra maiúsculo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">rust_function</span><span class="p">(</span><span class="n">s</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">String</span> <span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Do something with the Rust string here
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">s</span><span class="p">.</span><span class="n">to_uppercase</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Simples, recebe uma referência pra um String e retorna outro String. Agora vamos ver a mesma função mas usando direto String de C em vez do String de Rust:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">ffi</span>::<span class="n">CString</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">rust_function</span><span class="p">(</span><span class="n">s</span>: <span class="nc">CString</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">CString</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Convert the CString to a slice of c_char
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">s_slice</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="n">c_char</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="n">as_ref</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Iterate over the slice and uppercase the characters
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">s_uppercase</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">c_char</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s_slice</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">iter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|&amp;</span><span class="n">c</span><span class="o">|</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">to_ascii_uppercase</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">collect</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Create a new CString from the uppercase characters
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="n">s_uppercase</span><span class="p">).</span><span class="n">unwrap</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu não sou super fluente em Rust então não sei se tem jeito melhor de fazer isso, mas veja a diferença. Agora o parâmetro é direto um string de C e devolve um string de C também. Um objeto de String de Rust tem métodos conveniente como &ldquo;to_uppercase&rdquo; pra transformar tudo em maiúsculo. Mas pra trabalhar em cima de um String de C, precisamos manualmente iterar caracter a caracter do array e transformar em maiúsculo com o método &ldquo;to_ascii_uppercase&rdquo;.</p>
<p>Pra fazer código Rust compatível com estrutura de C sem ficar convertendo em objetos nativos de Rust, que são mais convenientes mas duplica memória, precisamos praticamente escrever C usando sintaxe de Rust. Não vai ser o código mais bonito do mundo, mas em casos mais complexos que esse certamente vai ser melhor do que escrever C na mão, porque podemos contar com os recursos mais avançados de gerenciamento de memória e segurança que o compilador do Rust nos fornece. Mas se alguém tinha dúvidas de como trabalhar com Rust e C, por exemplo, na kernel do Linux, esse é um exemplo besta que ilustra como é mais ou menos.</p>
<p>Agora é minha opinião, mas dado o que expliquei até agora, o que eu penso de linguagens é o seguinte: se quiser fazer programação de sistemas, ou seja, coisas de baixo nível como drivers de dispositivo ou mesmo componentes de infraestrutura como a stack TCP, o certo é usar C, C++ e agora Rust ou até linguagens meio desconhecidas mas  de mais alto nível e compiladas como Nim ou a linguagem D, que como o nome diz, foi pensado pra ser sucessor de C mas nunca conseguiu  popularidade, talvez porque Rust e Go surgiram no meio do caminho e meio que roubaram os holofotes.</p>
<p>Numa camada acima vem construção de ferramentas de administração e infraestrutura. Antes muitos faziam bibliotecas em C como um oniguruma pra regex, e pra facilitar o uso, criavam clientes com linguagens dinâmicas de script como Python ou Perl da vida. Mas Rust e Go tornaram menos dolorido escrever esse tipo de software. A sintaxe é mais moderna e limpa do que C ou C++ então não é incomum que não precise mais fazer scripts de Python por cima pra facilitar o uso. Exemplo disso é um Docker, feito em Go. Antigamente alguém poderia criar um libdocker em C e um script chamado Docker em Python que integra com essa lib pra ser a ferramenta de linha de comando. Agora podemos usar só Go ou só Rust.</p>
<p>Go e Rust geram binários quase tão eficientes quanto C ou C++ e no caso de Rust até mais eficientes e mais seguros em muitos casos. Nos últimos anos o mundo Linux ganhou várias ferramentas como o terminal Alacritty feito em Rust que eu gosto bastante, e tem esta página com várias ferramentas alternativas escritas em Rust que eu uso no dia a dia como o &ldquo;exa&rdquo; que substitui o &ldquo;ls&rdquo;, o &ldquo;procs&rdquo; que substitui o &ldquo;ps&rdquo;, &ldquo;fd&rdquo; que substitui o &ldquo;find&rdquo; e muito mais, depois dêem uma olhada.</p>
<p>Depois entramos na categoria que não expliquei muito que são sistemas distribuídos.  São softwares como bancos de dados ou sistemas de armazemento de volumes grandes de dados. Aqui eu vejo muitas soluções que saíram do ecossistema Apache, como Apache Hadoop, Apache Solr, Apache Spark, Apache Kafka, Apache Cassandra e muito mais, todos feitos em Java. Precisava ser em Java? Não, por exemplo, existem alternativas como a DataStax que é Cassandra escrito em C++.</p>
<p>Mas o que surgiu desse esforço massivo da Apache foi um ecossistema de bibliotecas e ferramentas que facilitam o desenvolvimento de sistemas distribuídos, como o famoso ZooKeeper que é o componente responsável pelo consenso entre diversos nodes de um sistema distribuído. Se isso soou grego, pensa assim. A grande vantagem de um banco de dados Cassandra é conseguir aumentar a capacidade adicionando novos servidores no cluster. O sistema se coordena pra receber esse node e passa a distribuir o armazenamento e processamento pra ele. Se um node falha, o sistema de consenso lida com isso também, via eleições por exemplo. São soluções pra quem precisa lidar com petabytes de dados, não é pra tech startup pequena sair usando.</p>
<p>A grande vantagem do Java é ser mais produtivo e menos doloroso do que C++. Uma coisa é fazer algo relativamente &ldquo;pequeno&rdquo; em quantidade de linhas de código como uma ferramenta de linha de comando ou driver, outra coisa é criar um software massivo com milhões de linhas de código pra lidar com processos complexos como coordenação de dados massivos em nós de um sistema distribuído. Java foi a escolha certa na época, talvez a única escolha. Pouca gente conhecia Erlang e a sintaxe também nunca foi muito familiar pra quem se acostumou a herança de sintaxe do C. Hoje em dia um novo Cassandra poderia ser feito ou em Elixir ou em Go, mas sendo sistemas super complexos e já maduros, não existe motivo pra reescrever tudo nessas linguagens.</p>
<p>Erlang foi uma oportunidade perdida e Elixir apareceu uns 5 ou 10 anos mais tarde do que deveria. Se tivesse aparecido no começo dos anos 2000, certamente seria uma das linguagens mais usadas hoje. O ZooKeeper que eu falei, boa parte de suas funcionalidades existem por padrão no Erlang, que também usa uma máquina virtual e foi feito pra criar serviços distribuídos no mercado de telecomunicações nos anos 80. Ele estava muito à frente do seu tempo, muito antes de começarmos a nos preocupar com escalabilidade horizontal em tamanho de internet no fim dos anos 90.</p>
<p>Só que naquela época as opções era fazer tudo em C++, que é eficiente mas é masoquismo, ou C# mas aí  estava fechado no ecossistema de Windows da Microsoft, ou Java, que era multiplataforma e bem mais amigável do que C++. Por eliminação, escolhemos Java. Só que a JVM não foi criado pra sistemas distribuídos como Erlang, então empresas como Apache precisaram criar essa infraestrutura, componentes como o ZooKeeper, Storm, Flink.</p>
<p>Eu posso estar superestimando, mas acredito que se fosse fazer tudo que tem no portfolio da Apache em Elixir, seria mais produtivo e menos difícil hoje. Mas nunca vamos saber, porque a essa altura do campeonato, não vale a pena tentar reescrever um ecossistema inteiro. A Apache já resolveu esses problemas com Java e equiparou as funcionalidades como Erlang. Pra entender qual é essa grande vantagem do Erlang, assista essa minha palestra onde explico como isso funciona dentro da VM do Erlang.</p>
<p>Do ponto de vista de sistemas distribuídos, outra alternativa moderna é Go. Só que Go tem bem menos ferramentas do que Java ou Erlang, que tiveram muito mais tempo pra amadurecer. Assim como Java, primeiro precisaria criar a mesma infraestrutura. Algoritmos de consenso, sistema de arquivos distribuídos, sistema de processamento de batch, streams distribuídos, armazenamento em cluster e assim por diante, antes de conseguir fazer os equivalente Spark, Kafka ou Cassandra. De novo, é tudo especulação da minha parte, porque a realidade é que se eu precisar de sistemas distribuídos, vou olhar o ecossistema Java da Apache.</p>
<p>O que nos leva a criação de aplicações comerciais, como eu chamaria. Coisas que a maioria aqui assistindo provavelmente vai lidar mais. Aplicações web. Seja feito em Ruby on Rails, ou Django com Python, ou Laravel com PHP, ou Spring Boot com Kotlin, ou Express com TypeScript e assim por diante. Aqui vem uma divisor: os softwares que falei até aqui como uma kernel de OS, driver de dispositivo, ferramentas de linha de comando de Linux, ou até um Kafka ou Cassandra tem uma coisa em comum: o ideal é que eles mudem pouco, que a maior parte das melhorias sejam em pequenas otimizações de performance, segurança, estabilidade. Eu não quero meu banco de dados mudando tanto que a cada nova versão dá crash.</p>
<p>Já aplicações web, que lidam com consumidor final, seja um chat, ecommerce, rede social, ferramentas de produtividade como um Figma ou Canva, ele competem pela atenção e o dinheiro das pessoas. Pra competir, uma precisa adicionar rápido funcionalidades que superam do concorrente. Mesmo que apareça um bug novo que alguns vão reclamar, o importante é que dê pra subir correções rápido. Pra isso queremos linguagens que facilitem produtividade mais do que performance. Sim, daria pra fazer sistemas mais eficientes escrevendo 100% em Rust, ou Go, ou mesmo Java e Elixir, mas até certo ponto é mais fácil treinar pessoas em Python ou Javascript e elas se tornam mais rapidamente produtivas nessas plataformas.</p>
<p>Esse é o motivo de porque não jogamos fora Python ou Javascript: porque taxa homem é mais caro do que custo de CPU por hora. Essas linguagens são mais limitadas como mostrei antes: não servem pra desenvolvimento de coisas com drivers ou protocolos de consenso nem processamento de imagem ou video, mas são ótimos pra fazer código cola mais fácil de usar, importando binários feitos em linguagens mais eficientes como C ou Rust. Portanto podemos usar um Python da vida pra 80% das funcionalidades mais &ldquo;mundanas&rdquo; e usamos C, Go ou Rust pra fazer as partes mais difíceis e integramos com uma extensão usando FFI.</p>
<p>Daí temos o mundo mobile, Android e iOS. Pra aplicativos simples, pense num iFood, AirBnb, até um Facebook ou Instagram, podemos apelar pra frameworks mais pesados mas, de novo, mais produtivos como React Native ou Flutter, que evita que a maioria dos desenvolvedores precisem saber como o baixo nível funciona e produzam mais rápido. A maioria dos smartphones hoje já tem mais de 2 gigabytes de RAM, e dá desperdiçar um pouco.</p>
<p>Claro, se todo mundo usa React Native, todo mundo vai estar limitado nas mesmas coisas que ele suporta. Pra se diferenciar, fazemos a mesma coisa que falei no mundo Web: criamos componentes mais eficientes, de mais baixo nível em Swift pra iOS ou Kotlin pra Android e  integramos nesses frameworks. Quem souber o baixo nível sempre vai ser um profissional mais valioso. É obrigatório? Não. Mas você quer evoluir na carreira e ganhar mais no futuro? Então sim, precisa saber. Sabe tudo que vim mostrando de exemplo na primeira metade do episódio? Sim, aquilo tem que parecer arroz com feijão pra você se considerar um especialista no assunto.</p>
<p>Daí chegamos nas categorias mais hypadas e consideradas mais modernas como data science, machine learning e inteligência artificial. E aqui o mais importante é tirar o máximo proveito do hardware, em particular de conjuntos de instruções como AVX, SSE ou CUDA em GPUs NVIDIA ou OpenCL em GPUs AMD. Pra isso precisamos voltar pro nível de C++. Como expliquei antes, tudo de data science em cima de Python começa em bibliotecas como numpy e scipy, que são feitos em C e Fortran. Machine learning como um Tensorflow começa em coisas como CUDA e C++. Mesmo em javascript, depende de acesso à GPU via WebGL dos navegadores. Não é a linguagem a parte mais importante e sim acesso a esses componentes de computação paralela de vetores e matrizes de dados.</p>
<p>Eu disse no episódio anterior comentando sobre o ChatGPT que no mundo de deep learning, criar modelos como de geração de imagens do Dall-e 2 ou geração de textos como GPT3, custam astronomicamente caro e nenhum de nós vai ser capaz de criar nada perto disso. Só quem tiver acesso a conseguir trabalhar nos centros de pesquisa de uma OpenAI ou Microsoft da vida que vai ter essa oportunidade. Mas pra coisas úteis do dia a dia não precisamos de um GPT. Machine learning com tensorflow e modelos ordens de grandeza mais simples já são úteis pra escopos específicos. Detecção de movimento é um exemplo, pra aplicações de realidade aumentada.</p>
<p>Muitos não pensam nisso, mas algoritmos mais &ldquo;simples&rdquo; como de relevância que é basicamente classificação estatística já é extremamente útil pra coisas como ecommerce conseguirem mostrar &ldquo;produtos recomendados pra você&rdquo;. Ou sistemas como Elastic terem pesquisa fuzzy com relevância, como um mini Google, onde o usuário não precisa escrever os termos exatos, mas mesmo assim ele consegue devolver resultados relevantes. Tudo isso já existe e já usamos em diversos sistemas, de ecommerces a redes sociais. A maioria dos desenvolvedores nem sabe o que é um Elastic ou Solr e acha que vai entender como funciona um ChatGPT. Tá longe, bem longe.</p>
<p>Enfim, eu sei que o episódio de hoje talvez tenha sido denso, mas eram alguns pensamentos aleatórios que tinha na minha cabeça fazia tempo que queria dar dump. Se você é iniciante, não espere compreender tudo que eu disse de uma só vez, pesquise os termos que não conhecia, volte de novo. Quem já tem experiência já sabia tudo que eu falei, talvez dê alguns insights pra complementar. O objetivo não era dizer que essa ou aquela linguagem é a melhor ou a pior.</p>
<p>Cada uma estava no lugar certo na hora certa, não necessariamente foram escolhidas por serem as melhores. E mesmo surgindo coisas novas consideradas melhores, não saímos reescrevendo. Maturidade é sempre mais importante que novidade. Se ficaram com dúvidas mande nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal e não deixem de compartilhar o video com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>programacao</category><category>akitando</category></item><item><title>[Akitando] #117 - Linguagem Compilada vs Interpretada | Qual é melhor?</title><link>https://akitaonrails.github.io/2022/04/15/akitando-117-linguagem-compilada-vs-interpretada-qual-e-melhor/</link><guid isPermaLink="true">https://akitaonrails.github.io/2022/04/15/akitando-117-linguagem-compilada-vs-interpretada-qual-e-melhor/</guid><pubDate>Fri, 15 Apr 2022 17:34:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/SNyh-cubxaU"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Chegou a hora de finalmente entender qual a diferença de linguagens compiladas e interpretadas, linguagens estáticas e dinâmicas. Java é compilado? Javascript é interpretado? Qual a diferença?&lt;/p&gt;
&lt;p&gt;Hoje você vai ganhar uma fundação mais sólida pra entender linguagens da maneira correta e é o pré-requisito pros próximos videos onde finalmente vou discutir as linguagens mais famosas da atualidade.&lt;/p&gt;
&lt;p&gt;== Conteúdo&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;00:00:00 - Intro&lt;/li&gt;
&lt;li&gt;00:00:55 - Pré-Requisitos&lt;/li&gt;
&lt;li&gt;00:01:53 - Hello World em C e Java&lt;/li&gt;
&lt;li&gt;00:03:03 - ELF vs CAFE&lt;/li&gt;
&lt;li&gt;00:03:41 - 1a tentativa: compilador vs interpretador&lt;/li&gt;
&lt;li&gt;00:04:45 - Estudo de Linguagens&lt;/li&gt;
&lt;li&gt;00:07:37 - Análise Léxica&lt;/li&gt;
&lt;li&gt;00:10:59 - Análise Sintática&lt;/li&gt;
&lt;li&gt;00:14:39 - Abstract Syntax Tree (AST)&lt;/li&gt;
&lt;li&gt;00:15:48 - Notação Polonesa&lt;/li&gt;
&lt;li&gt;00:17:58 - Otimização de Bytecode&lt;/li&gt;
&lt;li&gt;00:22:55 - Pra que serve um Programador?&lt;/li&gt;
&lt;li&gt;00:27:04 - Linters&lt;/li&gt;
&lt;li&gt;00:28:00 - Backus, Naur, BNF e História&lt;/li&gt;
&lt;li&gt;00:31:57 - Parsers e &amp;ldquo;DOM&amp;rdquo;&lt;/li&gt;
&lt;li&gt;00:32:58 - Interpretadores e Máquinas Virtuais&lt;/li&gt;
&lt;li&gt;00:36:56 - Linguagens Dinâmicas&lt;/li&gt;
&lt;li&gt;00:38:58 - Otimização Binária&lt;/li&gt;
&lt;li&gt;00:42:52 - As Fases de um Compilador&lt;/li&gt;
&lt;li&gt;00:46:23 - Just-In-Time Compiler (JIT)&lt;/li&gt;
&lt;li&gt;00:50:33 - Linkers&lt;/li&gt;
&lt;li&gt;00:59:24 - JIT de novo&lt;/li&gt;
&lt;li&gt;01:02:30 - Google V8&lt;/li&gt;
&lt;li&gt;01:05:25 - Por que dinâmico em vez de estático?&lt;/li&gt;
&lt;li&gt;01:07:42 - 2a tentativa: compilador vs interpretador?&lt;/li&gt;
&lt;li&gt;01:10:45 - Bônus: Bloopers (novidade)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;== Links&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/SNyh-cubxaU"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Chegou a hora de finalmente entender qual a diferença de linguagens compiladas e interpretadas, linguagens estáticas e dinâmicas. Java é compilado? Javascript é interpretado? Qual a diferença?</p>
<p>Hoje você vai ganhar uma fundação mais sólida pra entender linguagens da maneira correta e é o pré-requisito pros próximos videos onde finalmente vou discutir as linguagens mais famosas da atualidade.</p>
<p>== Conteúdo</p>
<ul>
<li>00:00:00 - Intro</li>
<li>00:00:55 - Pré-Requisitos</li>
<li>00:01:53 - Hello World em C e Java</li>
<li>00:03:03 - ELF vs CAFE</li>
<li>00:03:41 - 1a tentativa: compilador vs interpretador</li>
<li>00:04:45 - Estudo de Linguagens</li>
<li>00:07:37 - Análise Léxica</li>
<li>00:10:59 - Análise Sintática</li>
<li>00:14:39 - Abstract Syntax Tree (AST)</li>
<li>00:15:48 - Notação Polonesa</li>
<li>00:17:58 - Otimização de Bytecode</li>
<li>00:22:55 - Pra que serve um Programador?</li>
<li>00:27:04 - Linters</li>
<li>00:28:00 - Backus, Naur, BNF e História</li>
<li>00:31:57 - Parsers e &ldquo;DOM&rdquo;</li>
<li>00:32:58 - Interpretadores e Máquinas Virtuais</li>
<li>00:36:56 - Linguagens Dinâmicas</li>
<li>00:38:58 - Otimização Binária</li>
<li>00:42:52 - As Fases de um Compilador</li>
<li>00:46:23 - Just-In-Time Compiler (JIT)</li>
<li>00:50:33 - Linkers</li>
<li>00:59:24 - JIT de novo</li>
<li>01:02:30 - Google V8</li>
<li>01:05:25 - Por que dinâmico em vez de estático?</li>
<li>01:07:42 - 2a tentativa: compilador vs interpretador?</li>
<li>01:10:45 - Bônus: Bloopers (novidade)</li>
</ul>
<p>== Links</p>
<ul>
<li>ANSI C Grammar (<a href="https://www.lysator.liu.se/c/ANSI-C-grammar-y.html#compound-statement"target="_blank" rel="noopener">https://www.lysator.liu.se/c/ANSI-C-grammar-y.html#compound-statement</a>)</li>
<li>EcmaScript 2023 (<a href="https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations"target="_blank" rel="noopener">https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations</a>)</li>
<li>List of Java bytecode instructions (<a href="https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions</a>)</li>
<li>Why the New V8 is so Damn Fast (<a href="https://nodesource.com/blog/why-the-new-v8-is-so-damn-fast/"target="_blank" rel="noopener">https://nodesource.com/blog/why-the-new-v8-is-so-damn-fast/</a>)</li>
<li>V8 Bytecode.h (<a href="https://github.com/v8/v8/blob/master/src/interpreter/bytecodes.h"target="_blank" rel="noopener">https://github.com/v8/v8/blob/master/src/interpreter/bytecodes.h</a>)</li>
<li>LLVM Analysis and Transform Passes (<a href="https://llvm.org/docs/Passes.html"target="_blank" rel="noopener">https://llvm.org/docs/Passes.html</a>)</li>
<li>HHVM (<a href="https://hhvm.com/"target="_blank" rel="noopener">https://hhvm.com/</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Todo bom iniciante que se preza mais cedo ou mais tarde vai se engajar numa discussão sobre porque sua linguagem favorita é melhor que dos outros. E invariavelmente um dos argumentos que vai aparecer é que ser compilado é melhor, é mais performático, ou que ser interpretado é melhor porque é mais produtivo. E certamente todos que estão participando dessa discussão estão errados de maneiras que vocês nem imaginam.</p>
<p>Hoje quero falar um pouco sobre o que significa uma linguagem ser compilada ou interpretada e porque a maioria dos programadores interpreta isso errado (no pun intended). O objetivo não de hoje não é dizer qual é melhor, e sim porque sua linha de raciocínio não leva em conta diversos outros fatores que são mais importantes. No final do video você vai enxergar todas as suas linguagens de uma forma diferente.</p>
<p>(&hellip;)</p>
<p>Pra todo mundo que tá estudando ou já se formou em ciências da computação: não, eu não vou falar do livro do dragão de compiladores. Também não vou falar do livro de sistemas operacionais do Tanenbaum. Porém todo mundo que estudou essas duas matérias vai ter um aproveitamento melhor desse video. Aproveitem pra discutir os detalhes técnicos com seus colegas ou fazer mais perguntas pros seus professores na faculdade e nos cursos.</p>
<p>Compiladores e sistemas operacionais são conteúdos pra pelo menos um ano ou mais no curso de ciências da computação. Partes desses assuntos eu já expliquei em videos como o guia mais hardcore de introdução à computação e hello world como você nunca viu antes, e todos os outros na playlist de &ldquo;Como Seu Computador Funciona&rdquo;. Mas pra ter uma idéia, na faculdade, um dos trabalhos é fazer seu próprio compilador. Se nunca fez, depois do video fica de desafio tentar fazer o seu próprio. E pra isso já dou spoiler pra pesquisar tutoriais sobre a ferramenta ANTLR. Mas não vou falar especificamente de ANTLR hoje também.</p>
<p>Vamos começar do começo. Isso aqui é um Hello World em C. Agora, isso aqui é um terminal de Linux no WSL2 do meu PC e compilamos esse arquivo texto com o código em C num binário ELF compatível com Linux usando o compilador &ldquo;cc&rdquo;, que significa &ldquo;Compiler Collection&rdquo;. E pronto, isso é tudo que a maioria tem na cabeça quando se pensa em transformar código texto em um executável e no video sobre arquivos texto e binários eu explico como realmente as coisas funcionam e como um executável funciona. Mas vamos pra outro exemplo.</p>
<p>Agora, isso aqui é um Hello World em Java. Novamente vamos pro Terminal e é assim que se aprende na faculdade, roda <code>javac Hello.java</code> e ele vai gerar um <code>Hello.class</code>. E pra rodar é só fazer <code>java Hello</code> e voilá. Muita gente costuma dizer que Java é uma linguagem &ldquo;compilada&rdquo;, entre aspas. Só que olha o que estamos fazendo: chamando o executável &ldquo;java&rdquo; antes e dando como parâmetro a classe &ldquo;Hello&rdquo; que acabamos de compilar. Se fosse realmente compilado, no sentido clássico, deveria dar pra só fazer <code>./Hello.class</code>, mas isso não funciona, o sistema operacional não tem nenhuma idéia de como executar isso, porque um binário <code>.class</code> não é um executável em formato ELF.</p>
<p>Como tinha explicado no video de arquivos, pro Linux saber que um binário pode ser executado, ele precisa estar num formato chamado ELF, que significa Executable and Linkable Format. Em particular, todo binário ELF começa com a sequência que em hexadecimal seria 7f 45 4c 46, que propositalmente é ASCII pra string &ldquo;ELF&rdquo;. Isso é totalmente arbitrário e quem inventou o ELF só definiu que é assim e pronto. Mas se abrirmos os primeiros bits da classe Hello de Java, o que vemos nos primeiros bytes é a sequência hexadecimal <code>cafe babe</code>. Obviamente uma piada de café e java que o James Gosling escolheu pra serem os primeiros bytes de todas as classes Java.</p>
<p>Se definirmos que compilar é o processo de pegar um código fonte texto, por exemplo em C, passar por uma ferramenta como o &ldquo;cc&rdquo;, que vai cuspir um binário que o sistema operacional vai conseguir executar diretamente, então não, o Java não é compilado. Por outro lado vamos definir um interpretador. Um interpretador é um programa que vai pegar ou o arquivo texto do código ou uma representação intermediária, como esse &ldquo;.class&rdquo; e vai traduzir pro binário de máquina que, aí sim, o sistema operacional e o processador vão entender. Portanto, por essa definição, Java é uma linguagem interpretada.</p>
<p>Isso é o que você vai encontrar na bolhadev, e na verdade o que acabei de falar não tá nem totalmente certo, nem totalmente errado. Tanto C quanto Java são linguagens compiladas. E antes de explicar qual a diferença, preciso começar explicando o que é o processo de compilação. Esse processo tem diversos passos mas minha intenção não é escovar bits hoje, mas sim fazer com que vocês tenham um modelo mental fácil na cabeça pra não cairem em pegadinhas de discussão online e conseguir entender as principais diferenças.</p>
<p>O estudo de linguagens de programação é meio que um sub-conjunto do estudo de linguagens em geral, tipo português ou inglês. Em ciência da computação, na teoria formal de linguagens, as linguagens de programação como C ou Java ou Python são o que chamamos de linguagens regulares. Por sua vez, uma linguagem regular é uma linguagem formal que pode ser definida por expressões regulares, sabe o regex que você usa todo dia pra validar formato de emails ou cpfs? E por sua vez uma expressão regular pode ser definida como uma linguagem que pode ser reconhecida por um autômato finito. Älguns de vocês já devem ter esbarrado no termo &ldquo;máquinas de estado finito&rdquo;, que é outro nome que se dá a autômatos de estado finito.</p>
<p>Mas relaxa, nem eu sei tudo em todos os detalhes e não vou descer na matemática por trás de autômatos não, mas é pra saber que existe esse campo de estudo. Na prática, pense assim. Temos um texto em português. O que é um texto? É uma coleção de parágrafos. E o que são parágrafos? É uma coleção de frases. E o que é uma frase? É uma coleção de palavras. Palavras são coleções de letras separadas por espaço ou outras pontuações, como vírgula ou ponto.</p>
<p>Então, se eu juntar quaisquer letras, vou ter palavras? Se eu juntar quaisquer palavras, vou ter frases? Não. Existe uma ordem que funciona. Conjuntos de letras só formam palavras se elas existem num dicionário, que é uma coleção arbitrárias de conjuntos de letras, que a gente definiu. Se eu juntar as letras &ldquo;h&rdquo;, &ldquo;e&rdquo;, &ldquo;l&rdquo;, &ldquo;l&rdquo;, &ldquo;o&rdquo;, um atrás do outro, no dicionário em inglês, isso define uma saudação e uma forma de iniciar uma comunicação por telefone, por exemplo. Tire um &ldquo;l&rdquo; dessa palavra e já não significa a mesma coisa.</p>
<p>Pra formar frases, não pode sair juntando quaisquer palavras. Elas precisam obedecer uma gramática. Sujeito, predicado, adjetivos, pronomes, tempos verbais e tudo mais que fazem uma frase ter sentido. Se eu disser a frase &ldquo;não vai ter jeito&rdquo;, significa que não tem mais conserto, fodeu já. Mas se na verdade faltou uma vírgula nessa frase depois do não, então seria &ldquo;não, vai ter jeito&rdquo;, que é exatamente o significado oposto. Uma única vírgula faz diferença. Quem já programou e teve bug porque esqueceu uma vírgula, em português, também tem bug se esquecer uma pontuação.</p>
<p>Voltando pro código C. Pro computador, um texto, é só um linguição de bytes, um linguição de caracteres. Tem letras, tem espaços, tem chaves, mas não quer dizer nada. Mesmo pra você, ser humano assistindo, se nunca programou, esse texto também não quer dizer bulhufas. Eu preciso dar um jeito de fazer o computador juntar letras que formam palavras, que no caso chamamos de &ldquo;tokens&rdquo;, que fazem sentido. &ldquo;int&rdquo; é um conjunto de letras que faz sentido. &ldquo;printf&rdquo; é outro conjunto de letras que faz sentido. Então preciso conseguir quebrar esse texto todo em uma lista de tokens. Pra isso vou &ldquo;tokenizer&rdquo;, ou &ldquo;lexar&rdquo;, e isso é o trabalho de uma ferramenta que faz análise léxica.</p>
<p>Na realidade precisamos definir os lexemes da linguagem, o equivalente de dicionário. Podemos definir que dígitos são todos os caracteres de zero até 9. E definimos como não-dígitos, todas os caracteres de &ldquo;a&rdquo; até &ldquo;z&rdquo;, em mínusculo e maiúsculo. Também definimos que pontuação são todos estes outros caracteres como parênteses, colchetes, vírgula, ponto e tudo mais. Alguns desses caracteres podem repetir em outra definição. Parênteses podem ser operadores, assim como sinal de mais, de menos, asterisco. E assim por diante. Agora um analisador léxico como um chamado <code>flex</code> pode pegar meu código em C e entender que tem sinais, strings, digitos, operadores e traduzir isso numa lista de tokens.</p>
<p>Uma vez tendo essa lista de tokens, precisamos saber o que significam. Eu preciso que o computador entenda que, o que vai entre parênteses depois do token &ldquo;printf&rdquo;, é um argumento. Eu preciso que ele entenda que o &ldquo;return 0&rdquo; na última linha entre chaves se refere ao tipo &ldquo;int&rdquo; declarado no começo da função &ldquo;main&rdquo;. Elas precisam estar seguindo alguma regra. Uma vez tendo uma lista de tokens, precisamos checar contra uma gramática.</p>
<p>Antes de ficar teórico demais, deixa eu mostrar um exemplo prático. Pra isso fiz a linguagem de programação mais idiota do mundo. Ela não é turing complete, só aceita uma expressão no formato <code>1 add 2</code> pra somar, ou <code>4 sub 3</code> pra substrair. Só isso. Olha só, escrevi um programa nessa linguagem e salvei como &ldquo;hello.stupid&rdquo;, por que não? Pra rodar esse programa, fiz um interpretador em Javascript chamado &ldquo;js stupid&rdquo;. Vamos abrir pra ver o que faz.</p>
<p>As primeiras linhas é o jeito mais porco de pegar um argumento da linha de comando. Vou executar fazendo &ldquo;./stupid.js hello.stupid&rdquo;, igual o Java faz com &ldquo;java Hello&rdquo; antes, lembra. Pra isso botei um shebang na primeira linha que vai executar o Node automaticamente e carregar o resto do script nele. Depois pego o primeiro argumento e uso a biblioteca <code>path</code> pra formar o caminho absoluto até esse arquivo.</p>
<p>Daí uso a biblioteca &ldquo;fs&rdquo;, de filesystem, pra ler o arquivo. Se eu passar um arquivo que não existe vai dar um erro e sair. Senão eu pego o conteúdo do arquivo, limpo coisas no final como o caracter de nova linha, caso tenha, sabe o &ldquo;\n&rdquo;? E aí simulo o que seria o trabalho do tal analisador léxico, que vai tokenizar o conteúdo do arquivo.</p>
<p>Meu lexer é tão simples que é só um split de string de javascript. Ele quebra onde tem espaço e joga os tokens nesse array que chamei de &ldquo;tokens&rdquo;. Nesse ponto, converti o texto do arquivo numa lista e ainda converti o primeiro e o último token pra serem números.</p>
<p>Essa minha linguagem é tão besta que não precisa de um analisador sintático. Eu só faço um switch case e vejo o 2o token da lista. Se for &ldquo;add&rdquo; eu faço uma soma, se for &ldquo;sub&rdquo; eu faço uma subtração. E é isso aí. Eu posso adicionar quantas operações quiser seguindo esse formato. E no final, se colocar um operador que não tá declarado, vai dar erro de sintaxe e sair.</p>
<p>Se tiver um pouquinho de imaginação vai conseguir criar linguagens bem simplinhas usando só esse esquema. Mas dando spoilers já adianto onde que vai dar nó na cabeça. E se eu quisesse suportar uma expressão com mais de 2 números? Tipo fazer 1 + 2 + 3, como você faria? E se eu quisesse adicionar multiplicação e quiser que 1 + 2 * 3 dê a resposta correta? Note que multiplicação tem precedência, então 2 * 3 tem que vir antes de somar por 1. E dá pra ir complicando. E se eu quiser suportar variáveis? E se eu quiser suportar funções?</p>
<p>Obviamente isso é um problema já resolvido. E o primeiro conceito é separar a análise de sintaxe da execução propriamente dita, que é o que costumamos chamar de &ldquo;tempo de compilação&rdquo; e &ldquo;tempo de execução&rdquo;. Recapitulando, a análise léxica só dividiu o texto em tokens, e agora análise sintática vai dar significado pra esses tokens. Pra isso vai precisar de uma gramática que define o que são expressões, o que são funções, o que são condicionais. Por exemplo, podemos ver a gramática da linguagem C.</p>
<p>As ferramentas tradicionais que aprendemos na faculdade são lex ou flex, pra definir a análise léxica, e o bison ou yacc pra definir a gramática. Se procurar no GitHub é fácil de achar a gramática de todas as linguagens. A gramática do C em yacc é bem curtinha. Você imagina que definir uma linguagem deve ser milhares de linhas, mas na realidade não é mais que meia dúzia de page down, algumas centenas de linhas. Isso não é o compilador todo, só o parser, claro.</p>
<p>Se olharmos lá no fim da gramática, eis a definição do que é uma função de C, literalmente ele nomeia como &ldquo;function definition&rdquo;. E pode ser construído de 4 formas diferentes. A 1a são especificadores de declaração, seguido de declarador, seguido de lista de declaração e terminando com composição de statements. Em português não sei como chamam &ldquo;statements&rdquo;, alguns chamam de expressão, mas a definição de expression e statement são diferentes. Eu acostumei a chamar de statement então vai assim mesmo.</p>
<p>Mas o que tudo isso quer dizer? Esses nomes são como etiquetas, a definição de cada uma tá mais pra cima. Por exemplo, vamos ver o que &ldquo;compound statement&rdquo; quer dizer. Olha só, pode ser também 4 coisas: um bloco entre chaves vazias, ou uma lista de statements entre chaves, ou uma lista de declarações entre chaves e por último pode ser uma lista de declarações seguida de uma lista de statements, mas o principal é que estamos olhando pra declaração oficial, completa e não-ambígua do que o C chama de &ldquo;função&rdquo;. Não há espaço pra discussão, esta é gramática que define o C.</p>
<p>Agora o que é um statement list? Aqui vemos a definição e ela pode ser um statement ou vários statements, isso é meio que uma definição recursiva, por ser ela mesma várias vezes. Mas isso só define que pode ter um ou mais statements, agora precisamos ver o que é um statement. E agora sim, um statement pode ser de diversos tipos como labeled statement, compound statement que vimos antes, mas o interessante vai ser esse iteration statement.</p>
<p>E olha só, é isso que C chama de statements de iteração. Pode ser um while, pode ser um do while, pode ser for. E o for pode ser de dois jeitos diferentes, com ou sem uma expressão no final. E assim por diante. Se você tiver paciência pra ler a definição léxica e a gramática de yacc, tecnicamente é toda a sintaxe e semântica do C definida em 2 arquivos razoavelmente curtos, dá pra decorar e saber de cabeça. Claro, não é um iniciante que vai ler esses arquivos e entender tudo. Mas se alguma vez você já se perguntou, onde que tem a definição exata de uma linguagem? São nesses arquivos, e não em blog post.</p>
<p>Por exemplo, o Javascript tem uma definição semelhante. No site do EcmaScript podemos ver como é definido a versão mais nova 2023. E se pularmos aqui no menu pra Notação de Gramática, olha só como é a definição de um Variable Declaration, ou declaração de variável: é um Binding Identifier e um Initializer. Um Binding Identifier são tokens como &ldquo;const&rdquo;, &ldquo;let&rdquo; ou &ldquo;var&rdquo; e um Initializer é uma expressão de assignment como &ldquo;x = 1&rdquo;. E olha aqui embaixo, ele define como é um loop com for, um For Statement, que pode ser definido de 4 maneiras diferentes, todos com um Lexical Declaration e variando a forma da Expression.</p>
<p>Mas beleza, essa é a gramática mas e daí, pra que serve? O objetivo é quebrar seu código fonte, que é um texto, em uma listona de tokens e depois usar a gramática pra organizar esses tokens em uma estrutura de dados que podemos manipular programaticamente. O objetivo é transformar seu código em uma árvore, mais especificamente uma Parse Tree. Lembram? Eu sempre falo que uma das estruturas de dados mais importantes é uma árvore, por isso dediquei um video inteiro só sobre isso. No final seu código fonte vai ficar mais ou menos assim: (imagem)</p>
<p>Aliás, tudo que expliquei até agora sobre analisadores léxicos e sintáticos, o processo de transformar um texto de código em tokens e reorganizar esses tokens numa árvore, é o que muita gente chama de &ldquo;parser&rdquo;, mas na realidade a primeira etapa de tokenizar é feito por um lexer, e a segunda parte de pegar os tokens e transformar em árvore é feita por um parser. Pra simplificar, vou pular a explicação do que é uma Parse Tree e ir direto pra mostrar outra representação da mesma árvore, que é a árvore de sintaxe, Syntax Tree, ou mais especificamente uma Abstract Syntax Tree ou AST.</p>
<p>Lembra daquela minha linguagem de programação mais estúpida do mundo, que só aceita uma linha e só faz soma de dois elementos? Digamos que eu evolua ela pra fazer conta com mais de dois elementos e queira escrever aquele <code>1 + 2 * 3</code>. Essa é a notação que chamamos de infixa onde operadores como o sinal de mais e o asterisco vão no meio dos operandos, que são os números. É a notação, ou forma de escrever, que nós humanos estamos mais acostumados a ver, mas não é a única notação e nem a melhor.</p>
<p>Quem tá acostumado com as boas e velhas calculadoras de engenharia da HP conhece a famosa notação RPN ou notação polonesa reversa onde começamos digitando 3, depois 2, depois asterisco. Daí ele multiplica o 3 pelo 2, que dá 6. Continuamos digitando o 1 e só depois o sinal de soma, que vai somar o 1 com o resultado parcial 6, e isso vai dar 7.</p>
<p>Notação polonesa reversa é o que chamamos de pósfixa. Portanto notação polonesa é a préfixa. E numa árvore de sintaxe escrevemos de forma préfixa. O importante é saber que o que você considera &ldquo;bom senso&rdquo;, nem sempre é, tem formas melhores. Vamos ver como fica. A árvore pra essa conta começa com um nó raiz que seria o sinal de mais. Começamos pelo operador, por isso préfixo. Daí divide em dois galhos, o nó da esquerda poderia ser o asterisco e o nó da direita o número 1. Embaixo do nó de asterisco, divide um nó a esquerda pra ser o 2 e outro a direita pra ser o 3.</p>
<p>Essa representação seria o equivalente a escrever direto em polonês como <code>+(*(2, 3), 1)</code>. Já viram isso antes? Vamos mudar o símbolo de &ldquo;+&rdquo; e chamar de <code>somar</code> e trocar asterisco por <code>multiplicar</code>, daí ficaria <code>somar(multiplicar(2, 3), 1)</code>. Ficou mais claro agora?</p>
<p>É assim que você programa. A maioria das linguagens de programação, no caso especial de contas aritmeticas, como soma ou multiplicação, deixa você escrever da forma infixa, mas internamente, depois do parsing, o compilador tá mudando pra forma préfixa na árvore, assim como todo o resto das suas funções. Isso tudo foi pra ilustrar rapidamente que a forma que você escreve o código não é a forma como o computador vai executar.</p>
<p>Vamos ver como isso funciona. Vamos fazer outro programinha idiota em Java, que pegue os 3 primeiros parâmetros, converta o texto em número, que vai ser os números 1, 2 e 3 que usamos no exemplo anterior. Então precisa chamar o método estático <code>parseInt</code> da classe <code>Integer</code>. Daí vou imprimir no console com <code>System.out.print</code> a soma de a com b vezes c, exatamente como no exemplo. Agora compilamos com <code>javac</code> e podemos chamar <code>java Calc 1 2 3</code> e deu 7, como deveria. Nenhuma surpresa aqui.</p>
<p>Mas o que de fato executou? Pra quem não sabe existe a ferramenta <code>javap</code> que vem no JDK de Java que serve pra desassemblar, pra mostrar o assembly de bytecodes de Java. Lembra no episódio do guia mais hardcore de introdução a computadores que eu mostrei uma parte do assembly da CPU 6502 que era usado no antigo Nintendinho? Mesma coisa, a ferramenta javac vai passar nosso código por um parser, fazer algumas mágicas que já vou explicar e cuspir instruções binárias de máquina, o que comumente se chama de bytecodes ou assembly. E o javap só mostra esses bytecodes na tela de forma que nós humanos conseguimos ler.</p>
<p>Parece complicado, mas pra esse exemplo simples não é. Esse trecho que vai até a linha 21 são as instruções pra pegar os argumentos que passamos e converter em inteiro, aquele <code>Integer.parseInt</code>. Percebam que a gente só escreveu uma linha de código pra cada um dos três parâmetros, mas pra cada um ele gerou pelo menos 4 instruções, isso sem contar esse <code>invokestatic</code> que chama seja lá o que o <code>parseInt</code> faça.</p>
<p>Novamente, o que você escreve na sua linguagem sempre vira bem mais instruções depois de compilado, e esse é o objetivo. Antigamente, até antes dos anos 90, era super comum escrever direto em assembly, em linguagem de máquina, porque era mais eficiente. Não havia nem memória, nem processamento suficiente pra existir um compilador inteligente como os de hoje.</p>
<p>Mas agora os compiladores evoluíram tanto que geram assembly muito melhor do que é possível fazer na mão. Além disso os programas que escrevemos hoje são muito maiores, seria impossível escrever tudo em assembly. Se alguém já se perguntou se seria mais eficiente escrever tudo em assembly, não, não é, tirando raríssimas exceções, nenhum programador hoje é melhor que um compilador.</p>
<p>De qualquer forma, a parte que importa são as linhas 24 a 28. Aqui esse bytecode <code>iload</code> vai empilhando na Stack os números que converteu. Daí chama o bytecode <code>imul</code> que, como dá pra ver pelo nome, é a multiplicação. Ele desempilha os últimos 2 números, multiplica e empilha de volta o resultado. Chega na instrução <code>iadd</code> que desempilha os últimos dois valores na Stack, soma e empilha o resultado. Lembram? Eu expliquei pilhas no episódio de Hello World como você nunca viu. Se não sabe como um sistema baseado em pilha funciona, assista esse episódio depois.</p>
<p>Os mais espertos aí assistindo talvez tenham notado, mas porque eu fiz essa forma complicada de ter que passar os números como parâmetros na linha de comando? Por que não deixei hardcoded direto no código o <code>1 + 2 * 3</code>? Vamos fazer isso agora. Deixa eu criar uma nova classe chamada <code>Calc2</code> e fazer direto <code>System.out.print(1 + 2 * 3)</code>.</p>
<p>Aliás, ignorem a nomenclatura de classes que estou usando, tá bem porco mesmo só pra ir mais rápido, mas obviamente não façam nomes como &ldquo;Calc2&rdquo; em projeto de verdade, né? Enfim, agora salvamos, compilamos de novo com <code>javac</code> e podemos executar com <code>java Calc2</code> e devolve 7 de novo. Se for como no exemplo anterior, agora deve ter cortado aquele monte de bytecodes de parseInt mas deve ter as mesmas instruções de iload pra empilhar e imul e iadd pra multiplicar e somar, né?</p>
<p>Vamos conferir. Fazemos <code>javap -c Calc2</code> e olha só, ué, cadê a multiplicação e adição? Não tem, em vez disso tem aqui esse bytecode <code>bipush</code> que quer dizer &ldquo;empilhe este número inteiro na Stack&rdquo;, no caso o número 7 que já é direto o resultado de <code>1 + 2 * 3</code>. Sacaram? Na hora de compilar o javac já viu: cara, essa conta vai dar 7 sempre, nunca vai mudar, então pra que vou refazer o cálculo toda vez? Deixa eu já pré-calcular e dar direto o resultado. Assim ele economizou 3 chamadas de <code>iload</code>, e as chamadas pra <code>imul</code> e <code>iadd</code>. Em vez disso ficou só uma chamada <code>bipush</code> pra empilhar direto o resultado final 7 e daí já pula pra chamada que vai imprimir o texto no console. O compilador literalmente reescreveu meu código.</p>
<p>Lembram daquele meu interpretador pra linguagem mais estúpida do mundo? Ele pega o código do meu programa, tokeniza e coloca os tokens num array, e faz um switch case, se achar o operador &ldquo;add&rdquo; executa uma soma, se achar um &ldquo;mul&rdquo; faz multiplicação. E olhando pra isso, vocês poderiam pensar, &ldquo;ah, isso faz sentido, pra que precisa de tanto trabalho pra converter os tokens numa árvore e depois da árvore converter em instruções de máquina em vez de direto já cuspir instruções?&rdquo; E esse é um dos motivos. Vamos filosofar um pouco.</p>
<p>Não importa que sintaxe de linguagens você ache mais bonita ou mais elegante ou mais produtiva. Não importa se você gosta de usar chaves pra delimitar funções como no Java ou se prefere usar identação como em Python. Não importa se prefere dividir tudo em várias pequenas funções de poucas linhas ou prefira o jeito go-horse de entuchar o máximo de linhas dentro de uma função. Não importa se gosta ou não de colocar comentários detalhados antes de cada função. Tudo isso é totalmente irrelevante pro computador.</p>
<p>Depois que seu código for parseado e compilado, o que vai sobrar são instruções de máquina. Muito do que você escreveu vai ser reescrito, o compilador vai pensar &ldquo;porque esse idiota deixou um cálculo hardcoded que sempre dá a mesma resposta? vou reescrever e deixar direto só a resposta, foda-se.&rdquo; Sim, o compilador vai jogar fora tudo que você escreveu e vai reescrever tudo do zero da forma que ele acha mais eficiente. É isso que o computador vai ver no final e executar.</p>
<p>Mas então, tudo que sempre me falaram de Código Limpo, boas práticas, serve pra que então? Se no final o computador tá cagando e andando, pra que eu tô perdendo tempo organizando todo meu código, escolhendo nomes fáceis de entender e tudo mais? Porque você não está e nem deve estar programando pro computador, você programa pra outras pessoas. Entenda essa verdade: o seu trabalho não é programar pro computador e sim pra que outras pessoas entendam. Incluindo você daqui alguns dias ou meses.</p>
<p>Se como eu você curte retrogames, já deve ter assistido videos explicando os truques que programadores faziam antigamente pra conseguir extrair o máximo de máquinas de processadores super fracos como o Z80 ou 6502, com quase nada de RAM, na faixa de 2 kilobytes. Pensa que o Super Mario World inteiro, com todos os gráficos, fases e lógica ocupa meio megabyte. Em meio megabyte hoje você não consegue fazer nada.</p>
<p>Qualquer página web simples precisa de múltiplos megabytes. E isso porque antigamente ciclos de CPU e RAM eram hiper caras. Pelo mesmo preço que se comprava um Nintendinho nos anos 80, hoje você compra um Playstation 5. Literalmente, no começo dos anos 80 um nintendinho custava quase 180 dólares, que se ajustar pra inflação dá quase 500 dólares, que é o preço de um PS5.</p>
<p>Por isso antigamente era hiper importante economizar ciclos e bytes ao máximo, mesmo que fosse ao custo de dificultar a vida dos programadores, porque o custo do hardware era muito maior que o custo de programadores. 10 kilobytes a mais de RAM que desperdiçasse em 1983 era 200 dólares a mais. Por isso escovar bits era a coisa mais importante. Mas o tempo passou e agora 200 dólares você compra mais de 16 giga de RAM. Por isso que desperdiçar 1 ou 2 gigas de RAM, por mais absurdo que pareça, não é mais grande coisa.</p>
<p>Por outro lado o que subiu foi a hora de programador. Se o programador tem que gastar o dia todo pra descobrir que diabos é esse número 7 e que a conta original era 1 + 2 * 3, que se estivesse explícito no código ia custar 2 segundos pra entender, faz muita diferença. Toda boa prática de código é feito pra economizar hora de programador e não hora da máquina. E mesmo se você trabalha sozinho, as boas práticas e código limpo continuam fazendo sentido, porque o você de amanhã, às 2 horas da manhã numa emergência, vai te agradecer por ter feito tudo organizado e fácil de ler.</p>
<p>Lógico, não estou dizendo que você não tem que saber programar direito sabendo como a máquina vai se comportar. Nem o melhor compilador do mundo vai melhorar seu código bosta que dá &ldquo;select *&rdquo; numa tabela gigante do banco só pra pegar uma linha, porque ele não tem como saber, durante a compilação, que pode ter uma tabela de um milhão de linhas sendo consumido e causando vazamento de memória. Ele vai fazer o melhor possível, mas não existe limites pra programador ruim dificultar a vida do compilador.</p>
<p>Mas a tecnologia de compiladores não serve só pra cuspir binários otimizados. Aquela árvore de sintaxe abstrata, a AST é útil pro programador. Sabe quando no VSCode você instala uma extension pra Javascript que roda o jslinter e ele marca no seu código onde tem problemas e possíveis bugs? Essa análise é feita em cima da AST. Ele não tá olhando o texto do seu código.</p>
<p>Por baixo dos panos ele passou pelo parser do V8, gerou a estrutura de árvore e tá processando os nós dessa árvore. Os nós podem conter metadados que apontam de volta qual linha do código que representa, e assim dá pra analisar e devolver a análise pro editor mostrar bonitinho pra você. Todo linter funciona assim. Um linter ou ferramentas de análise estática, tentam te orientar pra escrever código melhor e apontar coisas que seriam ambíguas pro compilador, daí ele pode te dizer o equivalente a &ldquo;tem certeza que é isso que você quer fazer?&rdquo;</p>
<p>O que mostrei até aqui é a fase de parsing. Essa idéia de criar uma gramática livre de contexto veio de ninguém menos que John Backus, criador do Fortran, provavelmente a primeira linguagem de alto nível de sucesso comercial, lá nos anos 60. E não só ele fez a linguagem como se inspirou nos trabalhos de pesquisadores como o grande Turing ou Noam Chomski, cujo foco era linguagens no termo mais genérico, tipo língua portuguesa ou inglês, mas os resultados poderiam ser aplicados em linguagens de computador.</p>
<p>O Backus inventou essa idéia de uma linguagem que descreve outra linguagem, ou seja, uma meta-linguagem, e usou pra definir uma nova linguagem chamada IAL, que viria a se tornar o ALGOL. Muita gente costuma pensar em C como o avô das linguagens, mais por causa dos blocos com chave e statements terminando com ponto e vírgula. Outros consideram o C muito novo pra ser avô e o verdadeiro avô seria o COBOL.</p>
<p>Mas eu penso diferente. No máximo, COBOL é mais avô de coisas como linguagens de stored procedures de banco de dados. A real linguagem avô do que chamamos de &ldquo;linguagens de uso genérico&rdquo;, anterior ao C, é o ALGOL. Antes do C ser inventado, o Ritchie e Kerninghan trabalharam no BCPL que poderíamos chamar de linguagem &ldquo;B&rdquo; e pra mim a linguagem &ldquo;A&rdquo; é o ALGOL.</p>
<p>Antes que alguém comente, sim, eu sei, não é exatamente assim. ALGOL foi um linguagem criada por comitê e, como tudo que é gerado por comitê, ficou uma linguagem bloated, complexa, gorda. Daí na universidade de Cambridge em Londres surgiu o CPL que é o Combined Programming Language, mas que obviamente muitos poderiam chamar de Cambridge Programming Language, pra ser o &ldquo;ALGOL com os pés no chão&rdquo;. Acabou não sendo muito bem aceita, a intenção foi melhor que a implementação. Povo de javascript já viu episódios desse tipo, como yarn querendo ser npm melhorado, ou Deno querendo ser Node melhorado.</p>
<p>Ainda em Cambridge tentaram simplificar o CPL com o Basic CPL ou BCPL do Martin Richards, pra ser &ldquo;só as coisas boas do CPL&rdquo;. Poucos anos depois o Ken Thompson chefiou o projeto de UNIX pras máquinas PDP/11 e fez um compilador pra uma versão reduzida do BCPL que chamou de linguagem B. Mas tanto BCPL quanto B eram limitadas. B era alto nivel demais e como eu disse antes, isso custava recursos escassos e caros de hardware. Por esses e outros problemas, surgiu Dennis Ritchie pra reescrever a linguagem que seria a sucessora do B pra ser mais &ldquo;próxima da máquina&rdquo; e daí surgiu o C.</p>
<p>Mas independente disso, está claro que as raízes das principais funcionalidades que reconhecemos hoje numa linguagem de uso geral surgiram com o Algol e por isso podemos considerar que ela é a verdadeira linguagem A. E a linguagem C é a versão que realmente vingou e serviu de inspiração pra linguagens mais modernas como C++, Objective-C, que inspiraram depois Java, Python, C# e todo o resto que vocês usam hoje.</p>
<p>De qualquer forma, o cientista da computação Peter Naur reconheceu o poder da idéia de metalinguagem do Backus e como já existia o termo &ldquo;Chomski Normal Form&rdquo;, ele decidiu que a notação do Backus deveria se chamar &ldquo;Backus Normal Form&rdquo; ou BNF. Mas tecnicamente não era uma forma normal, como apontou o lendário Donald Knuth e foi ele que sugeriu que BNF deveria ser pra &ldquo;Backus-Naur Form&rdquo;, e é assim que chamamos até hoje.</p>
<p>Mas como falei antes, poucos programadores hoje, eu incluso, estudam as definições matemáticas por trás das notações de linguagens, com o rigor que deveria, porque ferramentas como flex, bison, yacc simplificam tudo isso e a gente tem mais que se preocupar com o design da linguagem propriamente dita, que começa em escrever a tabela de lexemes e a gramática no formato que mostrei antes. Em particular, se tiver interesse em estudar mais sobre geradores de parsers, a que eu vejo que povo considera mais moderna é o ANTLR, que por acaso é feita em Java.</p>
<p>Um parser não serve só pra compilar linguagens de programação. Pense um arquivo de configuração em YAML ou um JSON. Ambos não deixam de ser linguagens, só que declarativas e não de programação. Ambas precisam de um parser e de fato, pra transformar um YAML ou JSON num objeto de javascript, ou um Hash em Python, precisa de um lexer e de um parser.</p>
<p>Sabe quando você carrega um arquivo texto de HTML num navegador e agora dá pra inspecionar usando as ferramentas de debug? Você que é de front-end sabe disso, ele se transforma no tal do DOM ou Document Object Model. E o que é o DOM? É uma árvore, é resultado de passar o HTML por um parser. E como o navegador desenha as coisas na tela? Navegando pode Node a Node do DOM. O DOM está pra HTML assim como AST está pra uma linguagem de programação. E da mesma forma que você pode modificar nós do DOM, o compilador manipula nós do AST pra melhorar seu código e gerar binários mais eficientes.</p>
<p>Tanto o que você chama de compilador quanto o que chama de interpretador começam com um lexer e um parser. Mas a história não acaba aqui. Agora vem a parte interessante da história. Lembra quando fizemos o disassembly do binário compilado de Java? Ele não era uma árvore né, era uma sequência de instruções, o que em Java se chama de bytecodes. Depois que o javac gerou o AST, checou que a sintaxe tá correta e as dependências estão corretas, daí navega nó a nó da árvore e cospe os bytecodes correspondentes pra gerar o arquivo &ldquo;.class&rdquo; no final.</p>
<p>Vamos recapitular, o que é um bytecode? É literalmente isso, um código de byte. Cada instrução como aquela aaload, iload, imul, iadd são mnemônicos pra um código binário. Por exemplo, aaload é o hexadecimal 32 ou o binário 0011 0010. Lembra, a máquina hardware, a CPU, só entende binário e internamente tem instruções que são representadas por certas sequências de bits.</p>
<p>Assista o episódio de Hello World ou outros videos da série &ldquo;Como Computadores Funcionam&rdquo; pra relembrar disso. Mas agora vem uma pergunta, que diabos de instrução é isso de aaload, iload, imul. Isso não é assembly de x64, que é o que roda em Intel ou AMD aí no seu PC ou notebook. Também não são instruções de ARM como os Mac M1 ou Qualcomm do seu Android. Que diabo de instruções são essas? E aqui vem a parte que confunde a diferença entre um compilador puro e um interpretador. Essas instruções são pra uma máquina virtual que não existe em forma de hardware.</p>
<p>Se existisse um chip de Java - e nos anos 90 a Sun até queria mesmo fazer um chip de verdade Java -, essas seriam as instruções. Mas esse chip não existe, o que existe é a JVM, que literalmente significa Java Virtual Machine. Todo programa Java roda numa máquina virtual. Por similaridade, todo programa Python roda numa máquina virtual Python. Todo programa Javascript roda numa máquina virtual Javascript, como o Google V8 ou Mozilla SpiderMonkey. Todo navegador web vem embutido com uma máquina virtual. E sim, máquina virtual como um Virtualbox ou VMWare, já pararam pra pensar nisso?</p>
<p>A gente tá acostumado a pensar em máquina virtual só como um Virtualbox ou Parallels ou erroneamente um Docker - que não é máquina virtual. Pensamos como programas que podemos instalar num Mac M1 pra conseguir rodar outros programas compilados como binários de Intel e interpretar em tempo real pra rodar numa máquina ARM. Tecnicamente, toda máquina virtual é um interpretador, no sentido que ele interpreta uma instrução de uma máquina pra instrução de outra máquina.</p>
<p>Pra quem é de Python, quando chamamos <code>python -c compileall .</code>, ele vai pegar todos os arquivos texto com extensão <code>.py</code> e transformar em <code>.pyc</code> ou <code>.pyo</code>. Pra isso o código texto vai passar por um lexer, por um parser e no final o AST resultante é transformado em instruções pra uma máquina virtual de Python. Aí ele serializa e salva esse AST num binário &ldquo;.pyc&rdquo;. O que chamamos de &ldquo;interpretador&rdquo; é um caso especial de máquina virtual. Alguns também chamam isso de &ldquo;runtime&rdquo;, mas são só nomes diferentes pra mesma coisa. Pra todos os efeitos e propósitos, seu programa Python tá rodando numa máquina virtual. O Javascript do seu navegador ou no Node.js também.</p>
<p>Dentre várias coisas que podem ser diferentes é que normalmente quando falamos de interpretadores é quando temos algumas facilidades de desenvolvimento, como um REPL que é acrônimo pra Read-Eval-Print-Loop, que é a linha de comando que abre quando você digita <code>python</code> ou <code>node</code> no terminal e pode sair digitando código que ele vai executando imediatamente, ou seja, ele lê, faz evaluation, imprime o resultado e volta pro loop e fica nisso até apertar <code>Ctrl+D</code> pra sair. Aliás, a linha de comando do seu terminal, seja Bash, ZSH ou Fish, é um interpretador também.</p>
<p>Sendo mais específico, um interpretador costuma manter o AST num formato que podemos modificar, depois dos arquivos do programa terem todos passados pelo parser. Toda vez que dentro do console de Python a gente define um novo método, estamos adicionando nós na árvore AST. É isso que define o que muitos chamam de &ldquo;linguagem dinâmica&rdquo;. Mesmo Java, apesar de mais chatinho, tem como modificar o AST que tá em memória. Eu posso mandar o classloader da JVM carregar outros arquivos &ldquo;.class&rdquo; que ainda não tinha carregado, ou usar a API de Reflection pra modificar o código em tempo real.</p>
<p>Claro, apesar de parecerem máquinas virtuais, interpretadores são diferentes de um VirtualBox. Um VirtualBox da vida de fato tenta ao máquina esconder tudo sobre a máquina host por baixo, pra fazer os programas rodando dentro acreditar que estão numa máquina de verdade. Quando um programa lá dentro pede pra fuçar o disco, o Virtualbox vai dar pra ele um disco virtual e não acesso ao disco de verdade.</p>
<p>Quando o programa pede pra conectar na internet, ele não vai ter acesso ao TCP da máquina de verdade, mas sim à uma rede virtual interna, como se fosse uma rede externa separada que faz ponte com a rede da máquina de verdade. E assim por diante. Já um interpretador não tenta esconder nada. Quando o programa de python pede acesso ao disco, o interpretador dá acesso ao disco de verdade. Quando o programa pede pra abrir um socket de rede, ele passa pela rede de verdade.</p>
<p>Então tecnicamente sim, Java é uma linguagem compilada porque no final gera um binário com instruções de máquina. Só que não são instruções de Intel ou ARM e sim pra JVM, uma máquina só virtual. Por outro lado, ela é interpretada porque a máquina pra qual ela foi compilada não existe e pra rodar depende de um interpretador que entende essas instruções e vai converter pras instruções da máquina host de verdade, como um Intel x64. O mesmo vale pra Python, Javascript, Ruby, PHP. E eu sei, já estou vendo você aí já indo ferozmente pros comentários pra discordar, porque eu ainda não mencionei JIT, segura a onda aí!</p>
<p>Já vou voltar no assunto de interpretadores e máquinas virtuais, mas o importante é saber que depois da fase de parsing, o objetivo é transformar a árvore abstrata de sintaxe em instruções de máquina, seja lá pra qual arquitetura de CPUs: uma de verdade como x64 ou arm64 ou uma virtual como a JVM. A história não acaba aqui.</p>
<p>Mesmo o compilador de C como GCC ou o clang de LLVM não sai da AST depois do parsing e já transforma direto em instrução de Intel. Não, acho que hoje em dia praticamente todos os compiladores trabalham com uma representação intermediária. O compilador de DotNet, por exemplo, chama isso de Intermediate Language ou IL. No mundo LLVM isso se chama Intermediate Representation ou IR. No mundo do GNU Compiler Collection ou GCC que todo mundo conhece, ele trabalha com RTL que é o Register Transfer Language. No mundo de shaders como pra rodar em Vulkan no SteamDeck, existe o padrão Standard Portable Intermediate Representation ou SPIR-V. E o que diabos é isso de linguagem intermediária?</p>
<p>Agora é a parte da mágica que eu falei antes que vou simplificar bastante, entenda que essa parte é absurdamente extensa na realidade. Vou usar o exemplo mais besta de todos. Lembra aquele exemplo da calculadora que escrevi em Java pra dar print em <code>1 + 2 * 3</code> e depois de compilar, fizemos o desassembly pra ver as instruções e só tinha um <code>bipush</code> direto do resultado 7? Pois é, o compilador foi inteligente em saber que não precisa traduzir instrução a instrução, a multiplicação e soma e, em vez disso, já pré-calculou e deixou direto o resultado, economizando várias instruções. Ou seja, ele otimizou o código e deixou tanto mais rápido quanto economizou ter que guardar os números 1, 2 e 3 na memória.</p>
<p>Essa é a otimização mais trivial e besta de todas, mas o compilador tem a capacidade de fazer centenas de outras otimizações. Por exemplo, digamos que seu código tenha trechos que nunca usa. Dependendo da linguagem e agressividade da otimização, ele pode até escolher jogar fora esse trecho e nem traduzir. Sim, o compilador pode escolher jogar seu código fora. Ele pode simplificar chamadas. Se a ordem dos fatores não influir no resultado, pode mudar a ordem do seu código pra CPU processar de forma mais eficiente.</p>
<p>Essa fase de otimização pode passar por todo o código mais de uma vez, é o que se chama de &ldquo;passes&rdquo;. Isso pra conseguir fazer coisas como achar constantes duplicadas e fazer ambas apontarem pro mesmo lugar. Eliminação de código morto, ou seja, trechos que nunca são executados e podem ser eliminados. Tail Call elimination, pra remover chamadas recursivas que podem ser otimizadas. E muito, muito mais. Isso é o que o LLVM chama de Transformation Passes.</p>
<p>Entre aspas é como se você tivesse um Linus Torvalds e um John Carmack juntos, reescrevendo seu código da maneira mais otimizada possível. Na prática essa etapa é o real trabalho de um compilador e onde se gasta mais tempo e mais recursos, porque tem um gerador de código varrendo tudo que você escreveu, exaustivamente, várias vezes, e reescrevendo da melhor forma que se conhece sem quebrar, nem gerar bugs.</p>
<p>Agora, imagine o seguinte: tá vendo essa lista de transformações e otimizações? Imagine se toda vez que inventa uma nova linguagem, se precisasse reescrever todas essas estratégias tudo de novo pra linguagem nova. Seria um trabalho absurdo, que ia gerar dezenas de bugs, e as otimizações seriam ruins porque criar essas rotinas levou anos e milhares de horas homens pra aperfeiçoar até ficarem perfeitas. Por isso que o comum é que toda nova linguagem, no final do parsing, seja convertida numa linguagem intermediária, que é sempre igual. E essa linguagem costuma ser tipo um assembly pra uma máquina virtual, que não precisa se preocupar nesse ponto com as limitações de design e legado de uma CPU de verdade com um x64.</p>
<p>Daí todas essas estratégias de otimização são executadas nessa linguagem interdiária. Um compilador como GCC ou derivados de LLVM na verdade são programas grandões que podemos dividir em 3 grandes funções diferentes. O primeiro é o front-end, que tem a responsabilidade de pegar linguagens como C, C++, Go, Rust, Swift e fazer o lexing, o parsing, gerar a árvore de sintaxe e dela cuspir as instruções de máquina virtual intermediária, sem quase nenhuma otimização. Essas instruções intermediárias são o RTL, no caso de GCC, ou IR no caso de LLVM, ou IL no caso de DotNet. O clang de LLVM seria o front-end de C pra LLVM, o conjunto das definições de lexer e gramática do parser.</p>
<p>Agora o 2o programa pega esse IR não otimizado e gasta um tempão lendo, relendo e reescrevendo, e a saída vai ser outro IR agora totalmente otimizado. Esse programa do meio poderia ser chamado de &ldquo;middle-end&rdquo;. E relembrando, cada uma das otimizações ou transformation passes que ele faz poderia ser tema de um paper de Ph.D. Não são coisas triviais como o exemplo que eu mostrei. E finalmente temos o 3o programa, que chamamos de &ldquo;back-end&rdquo;, que vai pegar o IR otimizado e traduzir finalmente pras instruções da máquina de verdade, ou seja, converter de instruções assembly de IR pra instruções assembly de x64 ou arm64 da vida. E esse é o binário final que pode ser executado.</p>
<p>Toda vez que que eu quiser inventar uma nova linguagem é só fazer as partes de lexer e parser, fazer só a parte que é o front-end dessa nova linguagem. É pra isso que servem ferramentas como o ANTLR que eu mencionei antes. É isso que linguagens como Rust fizeram quando construíram o compilador em cima de LLVM. Ele pode deixar um compilador que já existe otimizar o que puder. Num primeiro momento, o criador de uma nova linguagem não precisa refazer tudo do zero, pode reaproveitar o que já existe, porque uma vez tendo IR, o resto é igual. A preocupação inicial dele é só fazer um parser que converte sua linguagem em IR, como um transpiler que converte TypeScript em Javascript, por exemplo.</p>
<p>Quando um designer de chips como uma Apple inventa um novo como os tais M1, ele só precisa fazer a parte back-end, a parte que pega o IR já otimizado e converte em instruções de máquina. Amanhã a Qualcomm lança um novo chip SnapDragon ARM, ele só precisa atualizar o back-end do compilador, todo o resto continua igual. A AMD lança a nova série 6000 do Ryzen, só precisa atualizar o back-end dos principais compiladores se teve alguma mudança nas suas instruções. Entenderam?</p>
<p>Eu expliquei isso no video de Apple e GPL, mas além obviamente do hardware, qual é o grande segredo da Apple em conseguir migrar tudo pra M1 tão rápido? É eles terem investido quase 2 décadas melhorando suas tecnologias de compiladores em cima de LLVM. Com isso conseguiram migrar de PowerPC pra Intel nos anos 2000 e com isso migraram de Intel pra ARM muito rápido. E tudo que só o compilador não consegue resolver, eles tem como desenhar no chip pra compensar. E com isso temos as performances absurdas que vocês assistem nos diversos reviews de como programas originalmente compilados pra Intel rodam liso em ARM sem recompilar, só sendo interpretados em tempo real.</p>
<p>Se você é um cientista da computação e tem vontade de criar uma nova linguagem, provavelmente vai começar fazendo um front-end pra LLVM. Assim saíram linguagens como Rust da Mozilla, Julia, Crystal e outros. Pouquíssimas empresas tem capacidade pra fazer um compilador do zero com todos os tipos possíveis de otimização. Java tem otimizador próprio desde a era da Sun e depois passou pra Oracle e tem dezenas de empresas que investem em pesquisa pra continuar melhorando. O DotNet tem a Microsoft, que também investe pesado em pesquisa. E no meio disso tem o Javascript e o V8 do Google.</p>
<p>Agora, qual a diferença de C, Java e Javascript? No final do dia quer dizer que C é compilado porque gera binário nativo de máquina, mas Java e Javascript são interpretados? Não, no final do dia, todos geram binário nativo de máquina usando estratégias diferentes, pra resolver problemas diferentes. A estratégia de C se chama ahead of time compiling ou AOT. E no Java e Javascript eles usam Just in Time compiler, o famoso JIT. E mesmo JIT tem diversas estratégias diferentes.</p>
<p>Vou fazer outro exemplinho bem besta só pra vocês terem uma imagem na cabeça. Vamos voltar pro exemplo em Java de fazer um cálculo. Quem lembra como calcula a circunferência de um círculo? Lembra? Duas vezes Pi vezes Raio? 2 pi r. Então vamos fazer uma nova classe, método estático main, e System.out.print, 2 vezes 3.141592 etc que é PI mas vou colocar poucos dígitos pro exemplo, e vezes 10 que é o raio que quero usar. Salvamos, saímos, compilamos com <code>javac</code>, rodamos e voilá, 62.83 bla bla é a circunferência pra um raio 10.</p>
<p>Se fizermos disassembly com <code>javap</code> olha aqui embaixo essa instrução <code>ldc2_w</code> que puxa uma constante do pool de constantes e manda pra stack, e no comentário gerado temos que essa constante é um double 62.83 bla bla que é o valor pré-calculado. Ele fez a mesma otimização que no caso do <code>1 + 2 * 3</code>. Top, é o esperado. Mas como eu disse antes, olha como essa continha no código é feio, mesmo sendo simples. Quem curte código limpo tem gatilho só de ver isso. Dá pra melhorar isso.</p>
<p>Vamos escrever uma nova classe. Eu poderia começar definindo uma constante double chamada PI e escrever ali, 3.141592. Mas isso seria idiota porque obviamente Java já tem uma constante estática double de PI, que tá na classe Math. Ah, mas como eu ia adivinhar? Sempre pense: coisas triviais assim, as chances são que já existe. Na dúvida procurar no Google, &ldquo;Java constant PI&rdquo; e olha só, nos primeiros links já vem Math.PI. Quando você não acha as coisas não é que não existem ou são difíceis de achar, você que não procurou direito.</p>
<p>Enfim, continuamos fazendo uma função estática que retorna double chamada <code>circunferencia</code> que recebe como argumento uma variável double chamada <code>raio</code>. Dentro calculamos o 2 vezes <code>Math.PI</code> vezes a variável raio. E agora sim, no método <code>main</code> vamos dar <code>System.out.print</code>, como antes, mas o argumento vai ser o resultado do método <code>circunferencia</code> passando o valor 10. Pronto, salvamos, saímos, compilamos com <code>javac</code> e executamos e tá aí, 62.83 bla bla como tinha dada na outra versão.</p>
<p>Compara a versão antiga com a nova. Ficou maior, mas a intenção do código tá muito mais clara. Se daqui 1 mês eu for editar esse código, dá pra saber o que ele faz imediatamente. E claro, dava pra ter feito bem mais bonito que isso, fazer uma classe chamada <code>Circulo</code> ou um módulo chamado <code>Geometria</code> ou qualquer coisa assim, mas hoje não é design de orientação a objetos, então não impliquem muito com isso hoje.</p>
<p>Mas vamos ver como fica fazendo disassembly com <code>javap</code> e olha que estranho, tem a instrução <code>ldc2_w</code> que puxa a contante 10 que é o raio que eu defini e puxa pra stack, daí invoca a função <code>circunferencia</code> e dá jump aqui pra cima. Daí faz outro <code>ldc2_w</code> pra puxar a constante de PI pra stack também, na verdade a constante de <code>2 * Pi</code>, veja o comentário que ele gerou mostrando que é o dobro de PI, então ele otimizou isso. Mas aí chama a instrução <code>dmul</code> que é multiplicação de double e desempilha os últimos dois valores que colocamos na stack pra calcular, retorna e finalmente dá <code>invokevirtual</code> no printf pra imprimir na tela.</p>
<p>Mas <code>2 * pi * 10</code> é tudo constante, porque ele não otimizou o suficiente pra arrancar fora essa função <code>circunferencia</code>, ela não seria desnecessária? Eu não disse que o compilador reescreve tudo pra ficar mais eficiente? Não dava pra ter pré-calculado como antes e só guardar a constante com o resultado final? Então, aqui que a coisa começa a complicar. Toda função que criamos no Java se torna uma interface. Se o compilador quisesse ser agressivo ele poderia arrancar tudo fora e deixar só a constante, porém a gente não sabe se ninguém mesmo vai usar essa função. Lembra? Java também é uma linguagem interpretada, dinâmica, cujo código pode ser alterado dentro da JVM.</p>
<p>Se pensar isoladamente só nessa classe, não teria problema, mas classes Java não vivem no vácuo, elas foram pensadas pra serem reusadas. Por exemplo, no mesmo diretório posso criar um novo arquivo e escrever uma nova classe assim, fazendo de novo um <code>System.out.print</code>, chamando <code>Calc3.circunferencia</code> direto assim mesmo e passando 20 como raio. Se eu sair e compilar só esse arquivo calc5.java, posso executar <code>java Calc5</code> e tudo funciona. Note que nem na hora de compilar e nem na hora de executar eu mencionei o Calc3, porque ele tá no mesmo diretório, então quando a JVM carrega a classe Calc5 automaticamente faz o Class Loader carregar o Calc3 junto. Mas ele não precisou recompilar o Calc3 de novo.</p>
<p>E se eu apagar o Calc3.class e tentar executar o Calc5? Agora vai dar a erro de <code>NoClassDefFound</code>. Faz sentido né? Java foi feito de tal maneira que eu não preciso recompilar tudo do zero toda vez que alguma coisa muda. Ele compila cada classe individualmente, e pra garantir que outras classes não quebrem, pelo menos mantém a interface entre eles estável, o que significa que não pode sair otimizando demais e eliminando métodos, mesmo se naquela classe ela não é usada. E mesmo se eu tivesse declarado como <code>private</code>, ainda assim não daria pra apagar o método, porque Java tem sistema de Reflection que permite manipular métodos privados. Portanto, qualquer otimização que quebre a interface não pode ser feita.</p>
<p>Antes de continuar, agora precisamos entender rapidamente o que é uma interface. No mundo binário, não existe o conceito de abstração, tudo são instruções e endereços. Quando precisa chamar uma função, a máquina na verdade vai guardar o endereço que estamos executando  na pilha, vai fazer o jump, vai executar o que precisa, e no final vai retornar pro endereço que tinha guardado, pra continuar de onde parou. Eu expliquei isso no video do Hello World.</p>
<p>Agora, pra isso funcionar, quando eu compilei a classe Calc3, o compilador precisaria ter anotado que a função <code>circunferencia</code> vai estar disponível num endereço X, faz de conta 9000 0000. Daí quando eu compilei a classe Calc5 que chama essa função, nesse ponto o compilador precisaria saber que precisa escrever um jump pro tal endereço 9000 0000. Mas lembram que eu não passei o Calc3 como parâmetro nem nada? Como que o Java sabe pra qual endereço tem que dar jump?</p>
<p>Aqui estou especulando, mas imagino que quando a JVM carrega essas classes, nesse momento que ela vai preenchendo uma tabela interna que diz, &ldquo;ok, carregar classe Calc3, ela tem um método circunferencia, vou colocar ela no endereço que tem vago aqui, 9000 0000&rdquo;. Depois carrega a Calc5 e quando executa a função <code>main</code> dela e de lá pede pra chamar a função <code>circunferencia</code>, daí a JVM intercepta essa chamada e consulta sua tabelinha &ldquo;hmmmm, essa função é a que eu coloquei no endereço 9000 000, pula pra lá&rdquo;. Portanto os endereços não estão nas classes, elas são atribuídas na hora que a JVM carrega elas.</p>
<p>Vamos abrir o disassembly de novo, começa executando lá da linha 0 e vai indo. Uma hora esbarra nessa linha 6, que tem um <code>invokestatic</code> passando um índice 15. Ele olha lá na tabela interna dele e vê &ldquo;ah, número 15 é o endereço 9000 0000&rdquo; e aí dá o jump pro lugar certo. Eu simplifiquei mas justamente porque os endereços finais são definidos quando carrega o módulo e não quando compila, dá pra compilar as classes independentes uma da outra, como eu fiz aqui, compilando individualmente os dois arquivos, porque a JVM é quem vai gerenciar os endereços em tempo de execução.</p>
<p>Porém, linguagens como C, C++, Swift, Go, Rust, Crystal, e outras não tem um interpretador ou máquina virtual pra controlar isso, portanto cada parte de cada módulo sendo compilado precisa já saber o endereço final das coisas, senão na hora de executar não ia achar nada. Como que faz?</p>
<p>E aqui vem outro conceito de compilador que vou simplificar ao máximo. Pensa, se eu tivesse um projeto com mil arquivos, onde uma função num arquivo pode chamar funções em outros arquivos, seria uma zona pra compilar. Toda vez que eu mudo uma linha num arquivo, precisaria recompilar todos os mil arquivos pra garantir que os endereços de todo mundo estão corretos e eu não apaguei uma função ou deu conflito de endereços ou duplicou endereços sem querer. Então na realidade, tanto o GCC quanto um clang de LLVM quanto um rustc de Rust, vão quebrar a parte final da compilação em duas etapas.</p>
<p>Se você é de Linux e já instalou um pacote que compila a partir do código fonte, já deve ter esbarrado num <code>Makefile</code>, que nada mais é do que um script que automatiza a função de checar dependências, se tudo que precisa pra compilar tá instalado. Pra você que é de Javascript é o equivalente a um package.json. Pra você que é de Python é tipo um requirements.txt. Ele chama o compilador pra todos os arquivos do projeto. O que costuma aparecer na tela enquanto compila? Um monte de arquivos <code>.o</code> sendo gerados.</p>
<p>Esses &ldquo;.o&rdquo; são objetos, simplificando, eles são os binários compilados a partir de código fonte de C de cada arquivo. Vamos abrir o hello.c lá do começo. Tão vendo esse <code>include &lt;stdio&gt;</code> na primeira linha, isso indica pro compilador procurar por um arquivo de cabeçalho chamado <code>stdio.h</code> que tem que estar no Path do sistema. Nesse arquivo vai constar a interface da função <code>printf</code> que usamos lá embaixo. Se olharmos nesse arquivo tá dizendo que o <code>printf</code> é uma função que retorna inteiro e recebe como parâmetro um ponteiro constante de char, basicamente um array de chars, e tem outros argumentos opcionais.</p>
<p>Sem entrar muito em detalhes sobre o C, ter essa interface permite o compilador saber se quando estamos usando a função <code>printf</code>, se tá da forma correta. Porém, no momento da compilação do arquivo <code>hello.c</code> pro objeto binário <code>hello</code> ele ainda não sabe qual é o endereço onde encontrar a função, porque ela tá numa biblioteca externa, a libc, que é compartilhada com outros programas no mesmo sistema que dependem dela. Praticamente tudo numa distro Linux.</p>
<p>No ponto onde chama o <code>printf</code> o compilador vai tipo deixar um post-it dizendo &ldquo;confirmar depois onde fica esse endereço&rdquo;. Se eu dependesse de outras bibliotecas externas, ia ficar deixando vários post-its pra confirmar depois. Pra ilustrar, vamos fazer o disassembly do objeto hello usando o <code>objdump -d</code>. Olhar assembly sempre é meio intimidante, mas não tem problema, só focar no pedaço que interessa que é a função <code>main</code> que começa aqui a partir do endereço 1139 em hexa. O <code>printf</code> é uma função então estamos procurando um <code>call</code>.</p>
<p>E no endereço 1147 temos um suspeito, um <code>call</code>, mas pra um endereço aqui perto, em 1030. Vamos ver o que tem lá. E olha lá, tem um jump pra esse outro endereço e um comentário que é pra função <code>puts</code> da glibc. Se não estou enganado, quando se chama o printf com o parâmetro opcional nulo, ele muda pra puts. Mas nessa linha, na primeira fase do compilador, quando ele ainda não sabe os endereços, vai ter algo como um espaço em branco pro endereço, um post-it pra segunda fase confirmar o endereço.</p>
<p>Essa segunda fase é o trabalho de um linker, no caso do GNU LD. Até agora eu não precisei chamar o linker manualmente porque o GCC faz isso sozinho pra gente. Mas se eu quiser manipular vários objetos já compilados e transformar numa biblioteca compartilhada, como um <code>.so</code> de Linux ou <code>.dll</code> de Windows, posso fazer isso com o comando <code>ld</code>.</p>
<p>Inclusive, o linker pode fazer otimizações em tempo de linking. Apesar do nome ser linker que é linkar ou grudar os pedaços, ele pode modificar mais do binário. É ele que sabe se é possível por exemplo fazer inlining. Lembra o exemplo besta de Java de circunferência? Se fosse em C, como ninguém mais chama aquela função, ele poderia simplesmente copiar o conteúdo da função e colar no lugar onde a função é chamada. Isso evita no mínimo uma instrução de jump e outra de return. Num programa maior tem muito mais ganhos.</p>
<p>No final, o compilador de C consegue analisar seu código e realizar um monte de otimizações e reescrever de uma forma otimizada, mas isso arquivo a arquivo independentemente, daí o linker reavalia  os binários gerados, liga os endereços de cada um onde precisa e ainda otimiza mais, o que diminui mais ainda o tamanho do binário, economiza mais memória e processamento na hora de executar, e por isso programas em C e similares como Rust são tão rápidos.</p>
<p>Linguagens como Java, Javascript, Python, e outros não tem o equivalente de um linker quando compilam do código fonte pra bytecode intermediário. Uma das razões disso é aumentar a reusabilidade desses binários, o que também torna a fase de compilação menos demorada que um C pra um projeto do mesmo tamanho. Porém o binário vai ser menos otimizado, tecnicamente mais lento do que o equivalente gerado por C. Então é isso? C sempre vai ser mais performático que qualquer coisa? Não é bem assim. E agora voltamos pra Just In Time Compiler, os JITs.</p>
<p>Quando você carrega um projeto em Java, ele inicia a máquina virtual JVM, daí o class loader começa carregando os arquivos binários <code>.class</code> do seu projeto e criando tipo uma tabelona interna que diz em quais endereços internos na memória estão quais classes e quais funções, de forma que quando uma classe pede pra executar uma função de outra classe, a JVM vai intermediar e arbitrar mandando executar a função correta, devolvendo o retorno e tudo mais.</p>
<p>Um interpretador ou máquina virtual substitui a necessidade de um linker estático, porque ele cuida disso dinamicamente. Então você pode imaginar que o C, que não tem esse intermediário já precisa saber de antemão o endereço de tudo. Se de fato toda vez a JVM precisar ficar checando os endereços nessa tabela virtual pra cada chamada de função, seria tudo lento mesmo. Mas claro que no mínimo ela faz cache disso pra ir mais rápido. Mas ela pode fazer mais. Ela analisa os códigos mais executados, por exemplo baseado em quais caches são mais pedidos, os &ldquo;pontos mais quentes&rdquo; do programa, literalmente o HotSpot, que é o nome da máquina virtual de Java quando ele ganhou capacidades de just in time compiling, ou JIT.</p>
<p>Se meu programa chamasse aquela função circunferencia diversas vezes sempre com o mesmo raio, em vez de toda vez executar o cálculo, o JIT vai ser esperto e cachear o valor final. Muitas das otimizações que eu falei que o linker faz no C em tempo de compilação, o JIT vai fazer durante a execução. Por isso que em Java você já tenha ouvido falar de &ldquo;tempo de aquecimento&rdquo;, ou warm up. Logo que inicia um servidor Java, ele vai ter uma performance X que pode até parecer baixa, mas deixe rodando um tempo, e o mesmo código começa a executar mais rápido. O JIT é um otimizador em tempo real, aplicando mais e mais otimizações nos códigos mais acessados.</p>
<p>Esse JIT, no final, tá traduzindo o bytecode do Java em instruções nativas de máquina de verdade mais otimizadas. É literalmente um compilador e mais ou menos um linkeditor que só trabalha em cima de código que realmente é necessário. Se seu programa for grande e tem partes que raramente são acessados ou ninguém nunca acessa, eles continuam lá sem serem otimizados. E o JIT vai fazendo cache do que vai otimizado, assim da próxima vez que alguém chamar essa parte, ele só executa o binário nativo de novo. E nesse ponto, esse trecho do código pode estar quase tão rápido como se tivesse sido escrito em C. Claro, não é só isso que determina a performance final, mas o JIT diminui bastante a distância entre traduzir bytecode Java de forma interpretada e um binário nativo compilado de C.</p>
<p>A mesma coisa acontece com Javascript no navegador. Navegadores baseados em Chromium, como o próprio Chrome, Microsoft Edge, Opera, Brave, todos acabam usando a máquina virtual Google V8 pra executar Javascript. V8 na verdade é o nome de um conjunto de tecnologias de compilador e máquina virtual, assim como Java ou Python. Internamente ele tem um lexer, um parser, que vai cuspir um AST e que por sua vez vai gerar uma representação binária intermediária em bytecodes específicos da máquina virtual do V8.</p>
<p>Sim, o que roda no V8 não é javascript, são esses bytecodes. Repetindo, elas são como instruções de um x64 de verdade, mas pra uma máquina javascript que não existe. Nesse arquivo bytecode.h tem declarado todos os bytecodes. Vou deixar nas descrições abaixo pra quem tiver curiosidade.</p>
<p>Uma ver convertido em bytecodes, o interpretador do V8, que se chama Igniter, começa a traduzir instrução a instrução. Não vai ser lento, mas também não vai ser a melhor performance. Assim como na JVM, ele vai ficar analisando a execução e perceber quais partes precisa otimizar. Digamos que sua aplicação web baixou trocentas bibliotecas, por exemplo o arquivo inteiro do jQuery. Mas na real você só precisa da função <code>$()</code> pra achar um elemento da página e a função <code>fadeToggle</code> pra criar uma transição animada de fade desse elemento. Todas as outras funções você não usa.</p>
<p>Se o V8 fosse burro e tentasse compilar tudo que sua página baixa, ele ia gastar um tempão pra otimizar a biblioteca do jQuery inteiro, quando na verdade sua página só usa meia dúzia dessas funções. Mas à medida que o usuário navega pelo site, o V8 acha os hotspots e agora é hora do Just in Time Compiler, do JIT, entrar em ação, no caso o Google chama o JIT do V8 de TurboFan. É ele quem vai gastar memória e processamento pra otimizar as partes mais usadas e gerar binário nativo de máquina otimizado, parecido com o que o Java faz, e jogar num cache em memória ou até em disco. E por isso programas em Javascript costumam consumir muita memória.</p>
<p>Por isso que tanto Java quanto Javascript conseguem atingir velocidades muito boas, às vezes próximas de C mesmo sem terem sido agressivamente otimizadas por um compilador antes. Mas por que se dar a tanto trabalho e não simplesmente compilar tudo antes como o C faz? E isso não tem uma resposta única, mas se eu fosse resumir é o que define a diferença entre linguagens estáticas e dinâmicas.</p>
<p>A vantagem de C é que os compiladores amadureceram muito nos últimos 40 anos e o binário nativo final que geram tendem não só a serem os mais rápidos em geral, mas também a ocupar bem menos espaço em disco e a consumir menos memória quando carregam. Por isso que a kernel do Linux, drivers e coisas assim tendem a serem feitos com C, porque eu quero o mais otimizado possível e que escale bem, pra rodar tanto num servidor de datacenter com centenas de núcleos de processador e terabytes de RAM quanto num mero Raspberry Pi com quase nada de recursos.</p>
<p>Uma vez que o binário tá compilado estaticamente, é tudo fixo e imutável, você não tem como mudar o comportamento dele. Sim, se tiver muita paciência pra explorar coisas como injeção de código direto na memória do processo ou fazer patches de binários, explorar exploits de segurança, aí você consegue mudar alguma coisa. Mas isso é na gambiarra. É o que a comunidade de mods de games faz também. Eles descobrem em qual endereço da memória que fica o valor de vidas e eu injeto outro valor lá. Já mostrei isso no video do Super Mario.</p>
<p>Por outro lado pense no seu navegador web. A qualquer momento eu posso inspecionar um elemento de qualquer página e sair manipulando. Eu mesmo, quando faço capturas de tela pra mostrar a imagem de um site nos videos, se tem uma propaganda muito grande, abro o inspetor, seleciono o elemento de propaganda e apago. E essa mudança se reflete na página imediatamente. Isso só é possível porque o navegador mantém a estrutura de dados que representa essa página em memória de forma acessível via Javascript. E como o código javascript não é estático, não é fixo, porque tem um interpretador que aceita código novo sem precisar reiniciar, dá pra fazer essas coisas.</p>
<p>A grande vantagem de linguagens dinâmicas é permitir esse tipo de flexibilidade. Se isso é bom ou ruim, depende do seu uso. Tem gente que acha que isso não deveria ser permitido, que uma vez compilado, deveria ser tudo fechado. Tem gente que acha que precisa ser tudo aberto, independente se vai ficar mais lento ou consumir mais memória. Como eu sempre digo, em programação a gente troca uma coisa por outra, sempre. Não existe resposta definitiva, tudo depende de pra que vai usar. E no caso de um navegador web acho que todo mundo vai concordar que ser aberto e dinâmico é muito mais prático e é por isso que a Web em si fez tanto sucesso.</p>
<p>Além disso linguagens dinâmicas como Javascript, Ruby, Python, Elixir, Java, C#, possuem capacidades muito potentes de metaprogramação, onde você pode escrever código que altera que está em execução. Praticamente toda linguagem com um interpretador possibilita isso. Java tem Reflection, Javascript ES6 tem APIs como Reflect ou Proxy, Python tem a função <code>type</code>, Ruby tem coisas como Refinements. E a vantagem de um interpretador é que podemos injetar novos bytecodes sem reiniciar o programa. Como a maioria dos interpretadores possuem algum tipo de JIT, esse novo código também pode gerar binário nativo otimizado.</p>
<p>Pra não complicar vou pular os detalhes sobre metaprogramação e a diferença com pré-processamento estático, templates como em C++, ou macros como em Rust. Estudem se tiver interesse, mas a conclusão de tudo isso é dizer que linguagens estáticas costumam ter um compilador que gera binários estáticos, cujo bytecode não é mais modificado facilmente. Linguagens dinâmicas costumam ter um interpretados justamente pra ter essas propriedades de dinamicamente modificar o bytecode e alterar o comportamento do programa em tempo real de execução. O custo disso é serem mais lentos, porque não puderam contar com a otimização agressiva de um compilador e linkeditor.</p>
<p>Mas hoje em dia essa diferença diminuiu drasticamente, porque quase todo interpretador vem com um compilador embutido que roda em tempo real, um Just in time compiler, ou JIT. No fim do dia, ambos acabam gerando binário nativo otimizado, um gera tudo antes, o outro gera enquanto roda. Um usa menos recursos, porque fez toda a compilação e otimização antes, e o outro usa mais recursos, porque vai continuamento compilando à medida que o programa é executado.</p>
<p>Como o video ficou super longo já, no próximo video vou dar uma pincelada em cada linguagem individualmente, mas antes vocês precisavam entender a diferença entre compiladores e interpretadores. Ser interpretado não implica ser sempre &ldquo;lento&rdquo;, só às vezes. Um exemplo, quem já ouviu falar de Elixir e Erlang provavelmente reconhece sua reputação de ser altamente escalável, tanto que é com ele que é feito o motor de comunicação por trás do Whatsapp, que é gigantesco. Mas nem todo mundo entende que Erlang, assim como Java, é uma linguagem interpretada, que roda numa máquina virtual, que é um tipo de interpretador, e que tem um Just in Time Compiler, ou JIT, pra acelerar as coisas.</p>
<p>Pela popularidade do Javascript, muita gente já sabe que as performances absurdas que ela consegue ter, o suficiente pra portar um game de verdade e rodar dentro do navegador é em grande parte graças ao JIT. Mas tem mais detalhes disso. E muito antes de Typescript e todas as linguagens transpiladas pra Javascript, um dos primeiros transpilers apareceu no mundo PHP com o HipHop do Facebook, que transpilava PHP pra C++. Hoje em dia eles pararam esse projeto e estão investindo mais na máquina virtual HHVM que é compatível até certo ponto com PHP 7. Python, consegue gravar os bytecodes da fase de parsing como arquivos <code>.pyc</code>, que são instruções pra sua própria máquina virtual. Ruby também gera bytecodes internamente. Enfim, tem bem mais coisas que eu vou guardar pro próximo video.</p>
<p>A intenção do video era só educar vocês pra não ficarem com cara de bobos argumentando coisas como &ldquo;ah, sua linguagem é interpretada, por isso nunca vai ser tão rápida quanto uma compilada&rdquo;, ou &ldquo;ah Java é melhor que Javascript porque ela é compilada&rdquo;. A grande verdade é que tecnologias de compiladores e máquinas virtuais evoluiu bastante nos últimos 20 anos e essas noções erradas que eram até corretas nos anos 90, já não é mais tão simples assim. Se ficaram com dúvidas mandem nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal e não deixem de compartilhar o video com seus amigos. A gente se vê, até mais.</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>ciencia-da-computacao</category><category>akitando</category></item><item><title>[Akitando] #93 - Hello World Como Você Nunca Viu! | Entendendo C</title><link>https://akitaonrails.github.io/2021/03/06/akitando-93-hello-world-como-voce-nunca-viu-entendendo-c/</link><guid isPermaLink="true">https://akitaonrails.github.io/2021/03/06/akitando-93-hello-world-como-voce-nunca-viu-entendendo-c/</guid><pubDate>Sat, 06 Mar 2021 10:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/Gp2m8ZuXoPg"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIÇÃO&lt;span class="hx:absolute hx:-mt-20" id="descrição"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Se você nunca viu C ou como um programa funciona de verdade no baixo nível, hoje é sua chance de ver todo o básico de uma só vez!&lt;/p&gt;
&lt;p&gt;Vou desde tipos primitivos, strings, arrays, stacks, heap, alocação de memória, structs, até minimamente entender o que está por baixo do que você chama de linguagem &amp;ldquo;orientada a objetos&amp;rdquo;.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/Gp2m8ZuXoPg"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIÇÃO<span class="hx:absolute hx:-mt-20" id="descrição"></span>
    <a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você nunca viu C ou como um programa funciona de verdade no baixo nível, hoje é sua chance de ver todo o básico de uma só vez!</p>
<p>Vou desde tipos primitivos, strings, arrays, stacks, heap, alocação de memória, structs, até minimamente entender o que está por baixo do que você chama de linguagem &ldquo;orientada a objetos&rdquo;.</p>
<p>ERRATAS</p>
<ul>
<li>em 00:06:50 falei errado o range de INT, o certo é de -128 a 127</li>
<li>em 00:06:06 eu falei certo e deixei a correção escrita errada. 64 bits, se você não precisar, desperdiça 7 bytes</li>
<li>em 00:40:54 eu falei que 255 bytes é 1/4 de 1 megabyte, mas é de 1 KILObyte.</li>
<li>em 00:45:41 eu coloquei <code>hello</code> no sizeof e ele pegou o sizeof do endereço e não do array.</li>
<li>em 00:38:40 eu não sei como deixei passar, mas quando falo de passar o string pras funções <code>f1</code>, <code>f2</code> não está duplicando toda a string e sim a referência pra ela. Se fossem valores primitivos como <code>int</code> sim, mas array só o endereço duplica mesmo.</li>
<li>em 00:57:55 quando dei copy e paste pro createPerson, eu esqueci de usar os argumentos pra fazer <code>person.age = age</code> por exemplo e ficou hardcoded. Viram?? Por isso copy e paste é perigoso, especialmente meia noite depois de estar cansado de passar 3 dias editando kkkkk</li>
</ul>
<p>LINKS:</p>
<ul>
<li>Integer (Wikipedia) (<a href="https://en.wikipedia.org/wiki/Integer_%28computer_science%29"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Integer_(computer_science)</a>)</li>
<li>Two’s Complement (Wikipedia) (<a href="https://en.wikipedia.org/wiki/Two%27s_complement"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Two%27s_complement</a>)</li>
<li>How numbers are encoded in JavaScript (<a href="https://2ality.com/2012/04/number-encoding.html#:~:text=JavaScript%20numbers%20are%20all%20floating,binary%20format%2C%20in%2064%20bits"target="_blank" rel="noopener">https://2ality.com/2012/04/number-encoding.html#:~:text=JavaScript%20numbers%20are%20all%20floating,binary%20format%2C%20in%2064%20bits</a>.)</li>
<li>FLOATING POINT VISUALLY EXPLAINED (<a href="https://fabiensanglard.net/floating_point_visually_explained/"target="_blank" rel="noopener">https://fabiensanglard.net/floating_point_visually_explained/</a>)</li>
<li>What Every Computer Scientist Should Know About Floating-Point Arithmetic (What Every Computer Scientist Should Know About Floating-Point Arithmetic (oracle.com))</li>
<li>IEEE-754 Floating Point Converter (IEEE-754 Floating Point Converter (h-schmidt.net))</li>
<li>Number.MAX_SAFE_INTEGER (Number.MAX_SAFE_INTEGER - JavaScript | MDN (mozilla.org))</li>
<li>Signed Binary/Decimal Conversion Using the Two&rsquo;s Complement Representation (Signed Binary/Decimal Conversion (ubc.ca))</li>
<li>C - Pointer arithmetic (C - Pointer arithmetic - Tutorialspoint)</li>
<li>Why Discord is switching from Go to Rust (<a href="https://blog.discord.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f"target="_blank" rel="noopener">https://blog.discord.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Seguindo meus episódios com o tema de fita de bits que venho explicando desde os episódios de introdução à computação até a história de Turing e Von Neumann, no penúltimo episódio eu expliquei sobre a diferença entre arquivos binários executáveis como os de formato ELF e arquivos texto e como tudo são fitas de bits ou o que eu chamei de linguição de bits. E hoje quero tentar explicar como podemos representar dados e manipular bits nessa linguiçona pra vocês conseguirem enxergar um pouco melhor como linguagens de programação lidam com dados.</p>
<p>Se você foi direto aprender uma linguagem de alto nível como Python ou Javascript, sem ter aprendido linguagens de baixo nível, vai eternamente ter dúvidas de &ldquo;o que&rdquo; exatamente a linguagem tá fazendo. Por que certos comportamentos estranhos acontecem do nada. Eu aprendi programação no começo dos anos 90 com linguagens de alto nível também, no caso foi Basic e depois dBase. Eu só fui entender como as coisas funcionavam quando comecei a estudar Ciências da Computação. Muita gente resolveu pular essa etapa e por isso vai ficar travado muito em breve. No video de hoje eu quero tentar abrir as portas pra coisas que você raramente vai encontrar num curso qualquer por aí.</p>
<p>Infelizmente é impossível eu resumir anos de ciências da computação num video só, então você vai precisar complementar o que eu disser hoje com a ajuda do seu professor da faculdade, ou pelo menos alguém mais experiente que você. Aliás, sei que tem vários professores que assistem os vídeos, então este é um episódio onde seus alunos vão precisar de ajuda.</p>
<p>Eu fiquei me indagando se devia quebrar este episódio em dois ou três mas resolvi entuchar tudo em um só mesmo pra tirar isso do caminho, então já aviso que vai ser super denso e bem difícil mesmo se você for iniciante. É normal não entender tudo só assistindo uma vez. Mesmo se você já sabe C, talvez goste de rever alguns conceitos de novo e até ajudar o povo nos comentários. Com tudo isso dito, respirem fundo, peguem papel e caneta pra anotações e vamos lá!</p>
<p>(&hellip;)</p>
<p>O objetivo hoje é destrinchar o linguição de bits de Turing e mapear o que acontece numa linguagem de programação de verdade. Vamos seguir o buraco de coelho e tentar enxergar a mulher de vermelho na Matrix. E a primeira coisa pra relembrar: na fita de bits, a máquina de Turing, ou o tal programa tem tanto binários que representam instruções pra sua CPU como também valores e dados, tudo junto um atrás do outro. Por exemplo, num hello world da vida, vai existir o valor &ldquo;hello world&rdquo; como uma cadeia de caracteres, o que chamamos de um String, e junto vai ter instruções pra mostrar esse String na tela de alguma forma.</p>
<p>String, em inglês é tipo um barbante, uma corda ou um fio. A forma mais simples de String é uma cadeia de bytes terminada com nulo de C. Se a gente encodar o &ldquo;hello world&rdquo; usando ASCII ou UTF-8, cada letra será representada por um código de 8-bits que é 1 byte. Então serão 11 bytes de tamanho mais o nulo no final. Mas se for em japonês &ldquo;haroo waarudo&rdquo;, isso é uma String de 7 símbolos, cada símbolo no alfabeto katakana vai ocupar 3-bytes, então serão 21 bytes de tamanho. Como expliquei no episódio anterior, quantos bytes cada símbolo ocupa depende da tabela de conversão entre glifos e caracteres. Esse é o encoding.</p>
<p>Uma String com caracteres ocidentais de 8-bits é particularmente interessante porque é igual a um Array de bytes. Array em inglês é tipo uma série ou conjunto de coisas. Uma lista sequencial, com cada elemento do array um atrás do outro na memória, sem buracos no meio. Essa distinção é importante e já vou voltar a falar de arrays daqui a pouco, mas antes precisamos falar de números.</p>
<p>Em algumas linguagens você vai ver duas palavras distintas, um byte e um char. Se o char for ASCII de 8-bits, então um char é a mesma coisa que um byte na prática, como é em C. Mas em C# ou Java cada char representa um caractere UTF-16 que tem 16-bits ou 2 bytes, portanto é um double byte. Não se pode achar que um char é sempre um pra um com um byte, só quando falamos de C. Como hoje só vou falar de C, então considere um char sendo 1 byte.</p>
<p>Cada linguagem suporta um certo número do que chamamos de tipos primitivos. Char é um exemplo de primitivo, que é um pequeno pacote de bits, sem metadados. Outro exemplo são números. No mundo real estamos acostumados a pensar em números como os inteiros indo de menos infinito até mais infinito, mas em computadores os números são limitados por seus tipos e temos diversos tipos de inteiros.</p>
<p>Na maioria das linguagens de programação a gente costuma lidar com Inteiros de 8, 16, 32 ou 64-bits, respectivamente Int8, Int16, Int32 ou Int64. Na linguagem Rust, por exemplo, é exatamente assim que é dividido e eles são chamados de i8, i16, i32 e i64. E o Rust ainda tem o i128 também. Um int8 é basicamente um byte ou um char de C.</p>
<p>No caso de C# o Int16 é chamado de Short, o Int32 é o Inteiro propriamente dito e Int64 é chamado de long. No caso do C é mais complicado porque se eu não estou muito errado o int dependia da arquitetura do CPU, então um int num computador de 16-bits era o Int16, mas num computador de 32-bits, se você recompilasse o mesmo programa, o int passava a ser 32-bits.</p>
<p>À primeira vista parece uma boa idéia, porque assim toda vez que as CPUs evoluem, bastaria recompilar o programa e ele automaticamente teria acesso a mais memória. Mas na prática isso pode levar a diversos tipos de bugs inesperados. Então quem decide o tamanho dos inteiros de C é o compilador. Mas nas linguagens mais modernas como C# ou Rust a declaração fica mais explícito qual tipo de inteiro você tá usando.</p>
<p>A razão de ter diferentes tipos de inteiros é a eficiência. Se você sabe que no seu programinha não vai precisar de números maiores que duzentos e cinquenta cinco, basta alocar um int8, ou 1 byte, e vai ser suficiente. Se você alocar um int64 sem precisar, vai desperdiçar 7 bytes que não vão servir pra nada. Se for uma variável só, não faz diferença, mas se for um array com milhares ou milhões de elementos, você vai rapidamente desperdiçar megabytes ou até gigabytes à toa.</p>
<p>Números inteiros ainda tem outra característica importante. Eles podem ser signed ou unsigned, ou seja, ter sinal ou não ter sinal. Se declarar um inteiro unsigned, sem sinal, ele pode ser usado pra números de 0 até hexa FF que é duzentos e cinquenta e cinco. Mas se for signed pode ir de negativo 127 até positivo 128. Quase a metade. Nesse caso o primeiro bit, que é chamado de bit mais significativo, e costuma ficar à esquerda dependendo do endianness do processador, é onde vai ficar o sinal. Se o primeiro bit for 0 então é um número positivo. Se for 1, então é negativo.</p>
<p>Daria pra fazer um video inteiro só falando de inteiros negativos e porque eles são interessantes, e isso é uma das coisas que recomendo pesquisarem a respeito depois. Mas vamos dizer o seguinte. Pra subtrair dois números com sinal, como 15 e 5, o computador primeiro vai enxergar eles em binário. Em 8-bits, no caso de 15 seria o binário 0000 1111. No caso de 5 seria 0000 0101. Você já aprendeu que subtrair um número é o mesmo que somar com seu negativo. Então poderíamos usar a máquina de somar que já mostrei como funciona no episódio de introdução mais hardcore a computadores. Reveja se não lembra mais.</p>
<p>Computadores usam o que se chama de método de complemento, que é a ideia de fazer uma subtração virar uma soma e usar o mesmo circuito de soma. Pra fazer isso ele transforma o segundo número no seu complemento de dois, ou two’s complement. Esse é um tema que você pode perguntar pro seu professor se ele não explicou. Pra achar esse complemento é simples, basta flipar todos os bits e somar 1, então o número 5 que é 0000 0101 vai virar 1111 1010 + 1 que é 1111 1011. O porquê de ter esse 1 extra no final é um dos motivos pra você estudar a teoria completa de two’s complement depois. Por agora vamos somar o 15 com esse 5 negativo.</p>
<p>Da direita pra esquerda, vamos lá, 1 mais 1 dá 2 que é 1 e 0 então fica zero e sobe um. Agora 1 mais 1 mais 1 é 3 que é 1 e 1, então fica 1 e sobe 1 de novo. 1 + 1 é 1 e 0 então fica zero e sobe um. De novo 1 mais 1 mais 1 é 3 então fica 1 e sobe 1. Agora tá fácil, 1 mais 1 fica zero e sobe 1. De novo, 1 mais 1 fica zero e sobe 1. Fica zero e sobe um. Fica zero e sobe um.</p>
<p>O último um que subiu a gente descarta. E o que fica no final é 0000 ou seja, como o primeiro bit é zero, o número é positivo e depois 1010 que é 8 mais 2 que dá 10. Exatamente 15 menos 5, como você já sabia. E com isso demonstramos que é possível subtrair números inteiros com sinal usando o método de complementos e reusando o mesmo circuito da máquina de somar binário.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>11111 111
 0000 1111
&#43;1111 1011
 ---------
         0
        1
       0
      1
     0
    0
   0
  0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As CPUs são feitas de tal maneira pra tirar vantagem de ser base 2 em vez de base 10 pra conseguir calcular de forma mais eficiente do que fazemos com número decimais. Multiplicar e dividir é mais complicado e se tiverem interesse estudem o algoritmo de multiplicação de Booth. Mas tudo isso foi só pra dizer que existe diferença entre inteiros com sinal e sem sinal.</p>
<p>Já expliquei que dentro da CPU existem diversos registradores, que é como se fossem variáveis globais fixas no hardware. No 6502 do Nintendinho tinha o registrador A, X, Y e outros, todos de 8-bits. E quando falamos de um Intel ou AMD moderno, eles têm registradores e instruções de 64-bits. Mas e quando precisamos fazer contas com números extraordinariamente grandes, acima dos exabytes que 64-bits comportam? Isso não é incomum, principalmente num mundo de Big Data e data science. Pra isso existem tipos compostos como o BigInteger, que tecnicamente não tem limites.</p>
<p>Se você tentar colocar um número de 128-bits num registrador de 64-bits vai dar overflow, ou seja, transbordar. Daí esse número seria truncado e metade seria perdido. Em vez disso, alocamos memória na RAM pra guardar esse número e, a grosso modo, fazemos a soma com a primeira metade, depois com a segunda metade e concatenamos o resultado. E sim, você pode fazer a soma de um número grande como duas somas de números menores, basta carregar o carry bit pra segunda soma.</p>
<p>Então precisaria de pelo menos 2 instruções de adição numa máquina de 64-bits pra somar dois BigInteger de 128-bits, mas é possível. Só vai custar pelo menos 2 vezes mais caro do que somar números menores de 64-bits. Tudo em computação tem um custo, não é automático nem tudo igual.</p>
<p>Isso dito, falei um monte de nomes que podem parecer complicados mas só precisa lembrar que são convenções de como representar números inteiros decimais em formato binário, com limites que vão de 8 até 64-bits dependendo da arquitetura nativa da CPU, ou mais se usarmos abstrações como BigInteger. E com esses números podemos fazer praticamente tudo que precisamos. Aliás, pense que a geração de microcomputadores de 8 e 16 bits foi quase inteira baseada só em números inteiros. Todo joguinho de nintendinho até mega drive é feito usando números inteiros.</p>
<p>A tela de um jogo tem 320 colunas por 240 linhas de resolução, que é um número inteiro. Um sprite de nintendinho tem 8 por 8 pixels ou 8 por 16 pixels. Pra mover um sprite de lugar basta ir somando de um em um pixel. Os sprites podem escolher entre paletas de 4 cores dentre 54 disponíveis. Pontuação de jogos, quantidade de vidas, número de fases, são todos números inteiros.</p>
<p>E fora dos games, em aplicativos de produtividade, seja planilhas, processadores de texto, tudo é baseado em números inteiros. Quantidade de letras ou palavras num texto é um número inteiro. Mesmo se você mexer com coisas como preços, que têm frações da moeda em centavos, basta multiplicar por 100 e fazer contas com inteiros e só no final dividir por 100 e truncar depois de 2 casas decimais. Então preços de produtos, salários, impostos, tudo pode ser calculado facilmente e mais importante, rápido, porque o hardware tem circuitos pra somar números inteiros.</p>
<p>Foi principalmente com o advento de coisas como computação gráfica, animação em 3D, que números fracionados começaram a fazer diferença no mundo comercial. Claro, na computação científica já precisavam fazer cálculos fracionados com muita precisão. Em previsões de meteorologia ou laboratórios. Mas isso era em nichos mais isolados e restritos com acesso a hardware especializado. Desde o começo do século XX já haviam formas de calcular números fracionados. O Z1 de Konrad Zuse, que mencionei na história de Turing e Von Neumann, já tinha esses conceitos. Inclusive seu Z4 foi o primeiro computador comercial nos anos 1940 a ter suporte a números fracionados.</p>
<p>Mainframes como o UNIVAC ou os da IBM dos anos 60 tinham suporte a calcular números fracionados também. Mas foi só quando começamos a adotar arquitetura de 32-bits no começo dos anos 80 que os diversos padrões começaram a convergir pro que viria a se tornar o padrão IEEE 754. E só a partir do meio dos anos 80 que começamos a adotar em microcomputadores domésticos.</p>
<p>Nos CPUs de 8-bits como o 8086 a Intel começou a desenvolver o que se chamaria de co-processadores matemáticos, como o 8087. Até a era do 386 a gente tinha opção de adicionar um segundo chip, como o 80387 que custava caro pra caramba e poucos programas usavam. Não lembro se foi já a partir dos 486 ou só nos Pentiums que paramos de precisar de co-processadores porque as instruções passaram a vir na própria CPU.</p>
<p>Precisamos entender pra que serviam esses co-processadores. Eu disse que até agora estávamos indo bem com números inteiros. Os CPUs dos anos 80 eram bons em processar números inteiros. Mas eventualmente a gente precisa lidar com números fracionados, com decimais. Em particular, números irracionais, ou seja, números que não podem ser representados por uma fração de números inteiros. Raíz quadrada de 2, o número Pi e muito mais. O Pi é o inteiro 3, depois tem uma vírgula e começa uma cadeia infinita de números um, quatro, um, cinco, nove, dois, seis, cinco, três e assim vai sem padrão nenhum até o infinito.</p>
<p>Se um número com decimais pode ser representado por uma fração, podemos usar números inteiros. Por exemplo, o número 0 vírgula 33333333 ad infinitum, é o inteiro um dividido pelo inteiro três. Assim muitos números com casas decimais infinitas podem ser representadas claramente com números inteiros finitos. Mas o mesmo não acontece com números irracionais como Pi. E computadores não tem memória infinita, então não temos como representar perfeito. Daí precisamos de uma aproximação. E a palavra importante aqui é “aproximação”, o que significa que ele não vai ser armazenado na memória como você acha que deveria e sim algo perto disso.</p>
<p>Essa aproximação é a especificação IEEE 754 que define o que chamamos de ponto flutuante. Esse tema é super chatinho e levaria um curso inteiro pra explicar e mesmo assim você vai ter dificuldade de entender, então vou simplificar grosseiramente hoje. Agora pouco eu expliquei como podemos ter inteiros com sinal e sem sinal. E quando tem sinal reservamos o primeiro bit mais significativo pra ser o sinal. Se for zero é positivo e se for 1 é negativo. Mas podemos reservar mais bits pra outras coisas também. Bits são o que a gente quiser que elas sejam.</p>
<p>Em países como Brasil as casas decimais são separadas por vírgula, mas nos Estados Unidos e em outros lugares se usa ponto em vez de vírgula. Por isso falamos em ponto flutuante e não vírgula flutuante. Você já deve ter ouvido falar do tipo <code>float</code>. O ponto separa a parte inteira do número da parte decimal e num computador fazemos esse ponto flutuar pra aumentar ou diminuir a precisão. Mas isso não é a história toda.</p>
<p>O IEEE 754 define vários tipos de float. O mais conhecido hoje é o binary64 que é um número de 64-bits ou 4 bytes. O primeiro bit é igual de um inteiro com sinal, ele define o sinal. Depois vêm 11 bits que representam o número do expoente e os demais 52 bits representam a mantissa. E o que é isso? É notação científica, mas na base 2. Na base 10 pra representar um número como 15000 poderia ser 1.5 x 10^4 ou 1.5e4, e esse “e” é de “expoente”. 1.5 é a mantissa e 4 é o expoente na base 10.</p>
<p>Um float é a mesma coisa, só que seria o primeiro bit de sinal pra positivo ou negativo, daí 2 elevado ao expoente armazenado nos próximos 11 bits, multiplicado pelo número representado nos 52-bits seguintes, que é a mantissa. O número inteiro máximo que pode ser representado nesse formato é 2 elevado a 53 que dá o número 9 quadrilhões e alguma coisa, que em particular, se você é de Javascript, é o que você já conhece como MAX_SAFE_INTEGER que, em binário, é representado assim aqui do lado: zero pro sinal pra ser positivo, o expoente só com o último bit flipado e a mantissa inteira de uns.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>0
00000000001
1111111111111111111111111111111111111111111111111111</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Da mesma forma, o que no Javascript você chama de MIN_SAFE_INTEGER que é o menor número que se pode representar assim, é só flipar ou inverter todos os bits do expoente e o primeiro bit significativo do sinal que temos o 9 quadrilhões e tanto negativo.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1
11111111110
0000000000000000000000000000000000000000000000</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O grande lance é que não conseguimos representar em notação científica de base 2 exatamente o número que normalmente queremos de decimal, sempre vai ter um erro de conversão. Por exemplo, podemos ir no site do link abaixo, que eu vou deixar nas descrições, que mostra essa conversão pra gente. Se tentarmos converter 0.1 na realidade vai dar zero ponto um zero zero sete vezes um quatro nove etc que é esse binário aqui do lado. É uma aproximação na base 2. Ou seja, depois do 1 vai ter uma pequena sujeira por isso tecnicamente é diferente de 0.1 exato, embora bem próximo se arredondar.</p>
<p>Mesma coisa se tentarmos converter 0.2 em binário, vai virar zero ponto dois, zero zero sete vezes de novo, dois nove oito etc. E esse numero float em binário é esse outro numerozão aqui do lado. E pra finalizar, 0.1 mais 0.2 deveria ser 0.3 se fosse base 10, mas 0.3 em binário vai ser esse numerozão aqui do lado. Com o erro de conversão na verdade vai ser zero ponto três zero zero seis vezes um um nove dois etc. Sempre vai sobrar uma sujeirinha em vez de ser zero zero zero só zero.</p>
<p>Se somarmos o 0.1 e 0.2 na instrução de soma binária do CPU, com os erros de precisão da conversão em binário, o resultado vai dar zero ponto três zero zero sete vezes, depois quatro quatro sete etc. Mas a conversão do 0.3 pra float dá zero ponto três zero zero só seis vezes em vez de sete e depois um um e não quatro quatro. É por isso que no Javascript e em diversas outras linguagens como Python, se você fizer 0.1 + 0.2 e tentar comparar com 0.3 vai dar false. Porque de fato quando converter de decimal pra float na base 2 ele vai ganhar essas sujeirinhas de erro de precisão na conversão e quando se faz conta com essas precisões ligeiramente erradas, a precisão do resultado também vai ser ligeiramente errado.</p>
<p><code>0.100000001490116119384765625 + 0.20000000298023223876953125 00111101110011001100110011001101 + 00111110010011001100110011001101 0.3 = 0.300000011920928955078125 = 00111110100110011001100110011010 0.1 + 0.2 = 0.300000004470348358154296875</code></p>
<p>Quem já tentou fazer muitas contas com float já se deparou com isso. E pra ser justo não é só em Javascript. Se você fizer a comparação de 0.1 mais 0.2 com 0.3 vai encontrar que é falso em Ruby, em Rust, em Python. Se não me engano, C# é uma linguagem que se dá ao trabalho de ajustar esse erro de precisão e devolve verdadeiro, mas no resto, que só faz a conversão direta pra binary64 do IEEE 754, vai ter esse problema nada intuitivo se você só pensar na base 10 e não na base 2. Você faz 0.1 mais 0.2 e no final não é exatamente 0.3. E isso é pior no Javascript por outra razão.</p>
<p>Em Javascript o único tipo numérico que existe é o float binary64. Não existem inteiros primitivos como mostrei antes. Então, em vez do inteiro máximo de dois elevado a 64, o máximo vai ser dois elevado a 53 como declarado no tal MAX_SAFE_INTEGER. Que é um número grande, mas não é o máximo que sua arquitetura suporta. E se você fizer conta com dinheiro, tipo desconto em produtos, ou calcular saldo baseado num extrato de conta, não faça contas com float, porque esse erro de precisão uma hora vai dar ruim. Pequenas sujeiras de precisão, em volume grande de contas, uma hora vai afetar bastante. No mínimo multiplique o valor por 100 pra trabalhar com centavos ou trunque depois de duas casas pra não ter essa sujeira final.</p>
<p>Melhor ainda, justamente por conta de bugs de precisão como esse, existe outro tipo de dado. Semelhante ao BigInteger que falei antes, a maioria das linguagens tem um tipo chamado BigDecimal. Assim como o BigInteger, pra fazer contas aritméticas, vai ser mais lento, porque vai precisar quebrar o numero em segmentos, fazer aritmética das partes e depois dar um jeito de concatenar o resultado, porque o numerozão não cabe nos registradores de 64-bits. No caso do BigDecimal, mesma coisa, ele vai permitir mais precisão e cuidar pra evitar essa sujeira de conversão.</p>
<p>A linguagem Javascript não tem um BigDecimal nativo, então você precisa baixar uma biblioteca que ofereça isso. Na verdade esse é um dos pontos onde Javascript é corretamente criticado. Todo número nele, seja um inteiro ou seja um float, em binário, é representado como um float binary64, o que eu pessoalmente acho tosco. Existe uma classe Number e ela deveria converter nos tipos corretos internamente, até pra pesar menos toda vez que precisa fazer cálculos. Dependendo de que versão da história você ver, esse é um dos efeitos colaterais da linguagem ter sido criada tão às pressas. E depois que já foi adotado assim, você tem dificuldade de voltar atrás agora. Pesquise sobre isso depois.</p>
<p>Enfim, o float que a maioria das linguagens usa é o tal binary64, de 64-bits que eu expliquei. Mas antigamente o que era chamado de float em C era a versão binary32. E a versão 64-bits era chamada de double. Então se algum dia você ver double, é o float de 64-bits. De qualquer forma, o importante era explicar que existia essa forma de se representar números e existem outras, como pra representar números complexos, mas não é importante pra hoje. Lógico, que fique de lição de casa pra vocês depois.</p>
<p>Agora vamos voltar ao tal do Array. Array é um conjunto que contém elementos do mesmo tipo, sequencialmente, um atrás do outro na fita de bits, como por exemplo array de números int8 ou float. Quando associamos uma variável a um array na realidade estamos apontando pro endereço do primeiro elemento desse array. E toda vez que tentamos acessar uma posição específica dentro desse array, basta pegar o endereço inicial e somar pelo tamanho do tipo do elemento que ele contém.</p>
<p>O importante de Arrays e Strings é entender que a sequência de bytes ou chars é necessariamente um atrás do outro, sem intervalos no meio. Se a gente aloca um array de 100 elementos int8, que é de 1 byte de tamanho, o array vai usar cento e um bytes de memória contígua. Esse um a mais é o byte nulo no final da sequência que indica o término dela, no caso de Strings. A beleza é que pra acessar qualquer elemento dessa sequência, sabendo a posição, basta pegar o endereço do primeiro elemento, o comprimento de cada elemento, no caso 1 byte, e multiplicar pela posição pra ter o endereço do que estamos procurando.</p>
<p>No exemplo do &ldquo;hello world&rdquo;, é um array de chars de 12 bytes contando o nulo no final. Se eu quiser puxar a 7a letra, basta pegar o endereço do primeiro elemento que é o &ldquo;h&rdquo; do hello, e somar por 7 bytes, já que cada char tem 1 byte de comprimento, e nesse 7o endereço vai estar a letra &ldquo;w&rdquo;, de &ldquo;world&rdquo;, como esperado.</p>
<p>Agora, isso só funciona porque cada elemento tem exatamente o mesmo comprimento, a mesma quantidade de bits. O requerimento de todo array é que todos os elementos dentro devem ser do mesmo tipo. Se quisermos armazenar números long, que tem 4 bytes, então todos os outros elementos precisam ser long. Daí um array de 100 elementos long vai ter 400 bytes de tamanho. E um outro problema disso é que um array tem tamanho fixo. Uma vez alocado, não tem como ser expandido.</p>
<p>Daí você pode ficar confuso, porque num Javascript e outras linguagens, se o array inicial tem 100 elementos e eu quiser adicionar mais, basta acessar direto a posição 101 e escrever lá. Por exemplo, vamos abrir o console de Javascript em qualquer navegador e declarar um array chamado <code>lista</code> com umas cinco letras, como nosso &ldquo;hello&rdquo;. Se eu tentar acessar a posição 4, vamos pegar a última letra &ldquo;o&rdquo;, e é 4 porque todo array que se preza começa da posição 0. Se eu tentar pegar a posição 5, vai vir undefined porque passei do fim do array. Mas eu posso tranquilamente gravar uma nova letra como &ldquo;exclamação&rdquo; nessa posição e boom, o array automaticamente “expande” pra comportar a nova letra, mas como isso é possível se eu falei que arrays tem tamanho fixo?</p>
<p>Na maioria das linguagens mais alto nível que C, tem estratégias, do grego estrategia. Se eu não disser o tamanho exato que quero, ela vai pré-alocar um espaço maior do que preciso. Dessa forma, se precisar de mais elementos depois do fim, tem espaço livre sobrando. E é uma estratégia quando você sabe que tem mais RAM do que precisa. Mas, se eu realmente chegar ao fim do espaço que a linguagem alocou pro meu array, o que ela vai fazer é alocar um novo array maior, copiar elemento a elemento pro novo array e descartar o antigo.</p>
<p>De novo, você não tá sabendo, mas tá acontecendo um custo extra de processamento por baixo dos panos. No dia a dia você nem percebe porque normalmente mexe em poucos arrays, mas em operações que tenham milhares ou milhões, aí você vai querer tomar mais cuidado com operações que fiquem mudando o tamanho dos arrays. Mesmo que a operação na linguagem seja só uma linha, por baixo dos panos ela vai fazer diversas outras, como duplicar seu array, que você nem fica sabendo e aí não entende porque a performance ficou tão ruim sem motivo aparente.</p>
<p>Mais ou menos a mesma coisa acontece com Strings. No C, sem bibliotecas extras, um String é literalmente declarado só como um array de chars. Em linguagens mais modernas o String é um tipo separado do Array, mas internamente a maioria usa Arrays, só que o tipo extra dá um suporte maior a coisas como conversão de encoding e tudo mais. Na prática, tanto arrays quanto strings, uma vez declarados e preenchidos, são fixos. E aumentar o tamanho deles pode envolver criar um novo array e copiar o conteúdo do antigo em cima do novo. Por isso um Java tem classes extras como StringBuffer pra lidar com strings que você vai precisar crescer depois.</p>
<p>Agora, tudo isso que eu vim falando até agora, array, string, ints ou floats, todos ocupam algum lugar numa memória, seja RAM, ROM, SSDs ou qualquer outra coisa. É tudo uma fita de bits, onde cada posição tem um endereço. Zero, um, dois, e assim vai. Cada valor em uso pode ser encontrado nessa fita com um endereço, é uma localização. O conceito importante aqui é entender que tudo tem um endereço nessa fita, que pode ser qualquer tipo de memória. Pode parecer complicado só porque endereços em máquinas de 64-bits podem ir até numerozões do tamanho de exabytes. E pra piorar escrevemos esses endereços como hexadecimais. Mas endereços são só isso: números.</p>
<p>Eu sei que tô andando bem rápido e já soltei bastante coisa até agora, mas se segurem que agora que a coisa vai ficar interessante. Recomendo que depois assistam tudo de novo com calma se isso é novo pra vocês, mas agora é uma boa hora pra eu descer mais ainda na escovação de bits. Uma coisa que não é claro pra iniciantes é onde ficam todas essas variáveis ao longo da execução de um programa. Então vamos partir de um exemplo bem besta em C. E não se preocupem se você nunca usou C, mesmo assim você deve ser capaz de acompanhar.</p>
<p>Em outros episódios eu já mostrei como isso funciona. A gente abre um editor de textos, escreve o codigozinho em C e usa o compilador <code>cc</code> que é o GCC pra ler esse código e dar output ou saída num binário executável em formato ELF, que o Linux consegue entender e executar. Se não sabe o que é isso, pelo menos assista o episódio da diferença de arquivos binário e texto, onde eu mostro como isso funciona.</p>
<p>O binário ELF tem um endereço específico onde fica a primeira instrução que vai começar a executar. No caso é o hexa 0x1000. Esse endereço é gravado no registrador chamado PC, que é o program counter ou contador de programa. É a partir disso que o CPU vai executando a instrução que tá apontado nesse contador. Uma vez executado o contador incrementa o endereço e aponta pra próxima instrução na fita de bits.</p>
<p>Bem a grosso modo, só pra ficar mais fácil de explicar, é como se esses endereços fossem os números da linha do código C no nosso editor de textos. Claro que não é, porque o que tá no binário não é o código que digitamos e sim o que o compilador traduziu pra linguagem de máquina, o linguição de bits. Mas pra facilitar a explicação, pense que no tal endereço 0x1000 ele pula ou dá JUMP pro endereço da função <code>main</code> do C e vai executando uma linha de cada vez dessa função. Sempre que eu me referir a endereço de programa como sendo o número da linha, é só uma metáfora pro endereço hexadecimal da instrução equivalente na memória de verdade.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#include &lt;stdio.h&gt;

void f2(char hello[]) {
    printf(&#34;from f2: %d\n&#34;, &amp;hello);
    printf(&#34;%s\n&#34;, hello);
}

void f1(char hello[]) {
    printf(&#34;from f1: %d\n&#34;, &amp;hello);
    f2(hello);
}

void main() {

    char hello[] = &#34;Hello World&#34;;
    printf(&#34;from main: %d\n&#34;, &amp;hello);
    f1(hello);

    return;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A função <code>main</code> é o ponto de partida de qualquer programa em C. Neste exemplo vai alocar uma String &ldquo;hello world&rdquo;. Daí pegamos a referência dessa variável com esse &amp; comercial na frente. Isso pega o endereço onde a string tá armazenada pra podermos imprimir na tela. Daí passamos a variável pra outra função que vamos chamar de <code>f1</code> e colocar no começo do arquivo. Aqui parece qualquer outra linguagem que você já usou, chamando uma função e passando um argumento entre parênteses. E precisamos declarar a função <code>f1</code> antes da <code>main</code> porque em C, pra usar uma função embaixo, ela precisa estar definida antes. Em outras linguagens isso não é verdade, depende do compilador. Mas no caso de C é assim.</p>
<p>Daí nessa função <code>f1</code> vamos receber como argumento um array de chars de novo e dentro vamos imprimir o endereço onde a variável desse argumento foi armazenado, e depois repassar essa variável pra outra função <code>f2</code>. De novo, definimos acima a função <code>f2</code> que, mesma coisa, vai receber um argumento de array de chars, imprimir o endereço da variável e por último imprimir o valor da variável que é o &ldquo;hello world&rdquo;. É um exemplo super besta, só fomos passando o mesmo valor algumas funções pra frente. Vamos sair do editor e usar <code>gcc</code> ou <code>cc</code> pra compilar esse código no tal executável ELF, e podemos rodar.</p>
<p>Agora a gente vê primeiro o endereço da primeira vez que declaramos a variável <code>hello</code> na função main. Vou repetir caso tenha ficado confuso. Se eu der <code>printf</code> direto na variável <code>hello</code> iria imprimir o valor “Hello World”. Mas como colocamos esse &amp; comercial na frente da variável, na realidade ele vai acessar o endereço onde o Hello World está gravado na memória. Tudo tem um endereço e em C, diferente de outras linguagens, temos acesso direto a esse endereço com essa notação.</p>
<p>Eu já falei do endereço de cada instrução do executável e na mesma memória estão valores que vamos criando, como essa String. Então executar um programa é executar a instrução que tá em algum endereço. E os valores que cada instrução vai usar, por sua vez, também estão em algum endereço na memória. E com esse &amp; comercial na frente da variável podemos fuçar o endereço. Pense no Hello World como o conteúdo de um arquivo texto e esse endereço como sendo uma URL de uma página web tipo <a href="http://hello.com"target="_blank" rel="noopener">http://hello.com</a>.</p>
<p>O segundo numerozão que aparece é o endereço do argumento de dentro da função <code>f1</code>, quando passamos o <code>hello</code> pra ela. Veja que o endereço é diferente, apesar do valor ser o mesmo. Ou seja, ao passar a variável pra função ela foi duplicada. E a terceira linha é a mesma coisa, outro endereço diferente que foi impresso de dentro da função <code>f2</code>. Porque quando a <code>f1</code> passou sua variável pra <code>f2</code> como argumento, essa variável foi duplicada de novo.</p>
<p>No final criamos 3 cópias desse String na memória ao longo do caminho, cada uma com endereços diferentes. Você pode ver que toda vez que damos <code>printf</code> passando a referência da variável ela imprime um número diferente. Toda vez que se passa uma variável de tipo primitivo como argumento de uma função, ela é duplicada. E isso não é só em C, mas na maioria das linguagens. Isso tem um nome, se chama “passagem por valor”, que é diferente de “passagem por referência”, que eu vou mostrar já já. A vantagem é que se a gente modificar o valor do argumento dentro da função, por exemplo a <code>f1</code>, o valor original antes de ser duplicada, por exemplo, na função <code>main</code>, não vai mudar depois que a <code>f1</code> der return.</p>
<p>Outro detalhe, o endereço é impresso negativo na tela porque o binário desse endereço começa com 1 e a função <code>printf</code> interpreta como sendo um integer com sinal, que como eu expliquei agora pouco, se começa com 1 ele acha que é um número negativo. Mas também por começar com 1 sabemos que se trata de um endereço na metade mais alta dos endereços da memória. Considere que o endereço mais baixo é o binário zero zero zero etc sessenta e quatro vezes e vai incrementando de um a um até o endereço um um um um sessenta e quatro vezes que é o endereço mais alto.</p>
<p>O espaço de memória que um programa enxerga é segmentado segundo alguma convenção. Pra facilitar a vida, num Windows da vida, ele começa a alocar memória pra drivers e coisas do sistema no final da metade alta, ou seja, começa no endereço binário um um um etc ou hex FFFF etc e vai diminuindo. Já programas de usuário, tipo seu navegador, o Spotify e tals, começa no início da metade baixa, mais próxima dos endereços que começam com zero.</p>
<p>Um adendo. Um computador antigo de 32-bits suportava um máximo de RAM de dois elevado a 32 que é pouco mais de 4 gigabytes. Quando passamos a adotar arquiteturas 64-bits, não dobramos de memória, mas sim elevamos ao quadrado. Por isso o máximo teórico é mais de 16 exabytes. Porém, nenhum chip comercial que a Intel ou AMD fabricam de fato suportam mapear esse tanto de memória ainda. Internamente as instruções lidam com valores de 64-bits mas nenhum hardware que você conseguir comprar, nem os AMD EPIC Threadripper da vida suporta tudo isso. Eles costumam ser capados em 42 ou 48 bits de endereços em vez de 64, ou seja, pouco mais de 280 terabytes. Que é coisa pra caramba, claro, mas só pra você saber que ainda não chegamos no máximo dos 64-bits em hardware comercial.</p>
<p>De qualquer forma, isso é a memória real. Mas como expliquei nos episódio de gerenciamento de memória, um processo rodando enxerga memória virtual, que é o sistema operacional mentindo pros nossos programas como se eles tivessem acesso a toda a memória teórica de 64-bits. É o que chamamos de memória virtual. Dentro desse espaço virtual, os endereços que começam do zero e vão subindo é o que chamamos de memória dinâmica, ou &ldquo;heap&rdquo; que é pra onde vai a maioria dos dados que seu programa carrega. Quando você carrega um arquivo em memória, ou quando se conecta num servidor de API e puxa JSON, ou quando pede dados no banco de dados, tudo vai pro heap. Heap em inglês é um montão, um amontoado.</p>
<p>Repetindo, lá no topo, nos endereços que começam com bit 1, é onde fica o segmento de memória reservada pra kernel, que é uma área exclusiva pra endereços de coisas do sistema operacional, mas o próximo segmento é o que chamamos de pilha ou &ldquo;stack&rdquo;. Se eu não tô muito enganado, todo processo começa com pelo menos 8 megabytes de memória alta reservada pra stack e ela pode expandir decrementando endereços pra baixo. Pode consumir até uns 2 gigabytes.</p>
<p>A heap é a mesma coisa mas do outro lado, começa na memória baixa já com pelo menos 1 megabyte reservado e se precisar de mais vai incrementando endereços pra cima. Lembre-se que o stack de um nintendinho de 8-bits tinha meros 255 bytes, que é do endereço hexa $0100 até $01FF.</p>
<p>Eu explico pra que serve essa stack no episódio da introdução mais hardcore a computadores mas vamos resumir usando o exemplo do hello world que fiz agora. Eu tenho 3 funções, a main, a f1 e a f2. Cada uma delas aloca espaço pra duplicatas da string hello world, daí imprimem o endereço onde a variável foi alocada e no final sai fora e retorna pra função que chamou ela. Aliás, em C, se uma função tem como tipo de retorno um <code>void</code> que é tipo <code>nada</code> em C, então nem precisa dar return, porque não estamos retornando nenhum valor. Vamos na ordem.</p>
<p>Ao executar o binário ele pede pra kernel do sistema operacional alocar espaço pro novo processo e inicializar seja lá o que ele precisa. A kernel vai usar o gerenciador de memória e alocar o mínimo necessário pro programa rodar e depois vai dando memória real pra ele à medida que for pedindo.</p>
<p>Depois ele pula pra primeira função que é a <code>main</code>. Agora cria a variável <code>hello</code> que é um array de chars com 12 bytes de tamanho. Isso é alocado na Stack. Agora eu chamo a função do C chamado <code>printf</code>. Não vou explicar como o <code>printf</code> funciona, mas vamos aproveitar pra explicar a mecânica dessa tal stack ou pilha. Toda chamada de função começa guardando o endereço atual de execução, o que está no tal contador de programas. No nosso pseudo-exemplo de endereços seria o número da linha do nosso código onde chama a função.</p>
<p>No momento que a função <code>main</code> chama a função <code>printf</code> ele faz tipo um bookmark do contador de programa e coloca no stack. Daí podemos substituir o endereço no contador de programa pelo endereço da função que tá chamando, no caso o endereço pra <code>printf</code>. Agora, dentro da <code>printf</code> vai alocar outras variáveis, faz de conta, umas variáveis x ou y quaisquer, que vão sendo colocadas na stack.</p>
<p>Finalmente imprime na tela o que precisava e no final dá <code>return</code>. Esse return começa a desempilhar o que alocou na stack até achar o último endereço que a <code>main</code> gravou, o tal bookmark do contador de programas, e dá jump, ou seja, pula pra ela. E pular significa colocar de volta esse endereço mais um no contador de programa, daí ele pode continuar da próxima linha depois de chamar a função. É mais ou menos isso que faz um <code>return</code>. O que tinha sido colocado na stack pelo <code>printf</code> foi liberado e voltamos a executar na próxima linha.</p>
<p>De volta ao <code>main</code> a próxima linha chama a nossa função <code>f1</code>. Mesma coisa, vamos empilhar o endereço dessa posição na stack. Como eu disse antes, ao passar a variável <code>hello</code> como argumento, estamos duplicando a variável, então vai empilhar essa duplicata na stack também. Dentro de <code>f1</code> vai chamar <code>printf</code> de novo pra imprimir o endereço dessa duplicata, por isso temos valores diferentes de endereços.</p>
<p>Já sabemos como isso funciona, então vamos pular os detalhes de <code>printf</code> pra não ficar repetitivo. Pode assumir que empilhamos e desempilhamos <code>printf</code>. Em seguida o <code>f1</code> vai chamar a <code>f2</code>, e novamente o endereço dessa linha vai ser empilhado na stack, a variável hello vai ser duplicada e empilhada na stack também, e de novo vamos chamar <code>printf</code>. por fim vai ter um return.</p>
<p>Esse <code>return</code> vai desempilhando até ver o endereço de volta à função <code>f1</code> e dar jump pra ela. Agora no <code>f1</code> não tem mais nada a não ser outro <code>return</code>, então continua desempilhando até o último endereço gravado da função <code>main</code> e dá jump de volta pra ela. E finalmente na <code>main</code> também temos um return. Agora é o último de todos, então ele sai do programa inteiro, daí o sistema operacional vai fazer a limpeza da memória desse processo e tudo termina com sucesso.</p>
<p>A razão dessa explicação longa foi pra mostrar no nível mais básico o que significa chamar funções e como variáveis são gerenciadas na stack. Toda vez que você chama funções e passa argumentos pra ela, esse registro de execução vai sendo empilhado na stack, que é uma pilha. Pilha é uma estrutura de dados super importante e uma das mais fundamentais junto com Queues, que é inglês pra filas. Elas são parecidas, mas não vou falar de queues hoje. E pilhas existem dois tipos, FIFO e LIFO.</p>
<p>A pilha que estamos usando aqui é do tipo LIFO, ou seja, Last in, first out. Então o último elemento que empilhamos é o primeiro que vai ser desempilhado. Esse é mais um tema que você precisa estudar em detalhes, como é implementada e como funciona em todos os casos. É uma das lições da matéria de Estrutura de Dados e Algoritmos que qualquer faculdade ensina e mesmo se você for autodidata deveria estudar.</p>
<p>De qualquer forma, você vai se lembrar que eu falei que essa stack costuma começar com um tamanho de 8 megas e pode expandir até uns 2 gigas. Isso é bastante pra programas de qualquer tamanho. Repetindo, um jogo de Nintendinho inteiro, seja Super Mario, Zelda ou Final Fantasy conseguia se virar com uma stack de meros 255 bytes, isso é um quarto de um mísero megabyte. Estourar esse stack é sinal de má programação e acredite, não é difícil estourar a stack. E quando ela estoura, seu programa crasheia.</p>
<p>O jeito mais fácil de estourar é via recursão. Recursão é uma função ficar chamando ela mesma sem parar. Vamos dar um exemplo, podemos fazer uma função chamada <code>f3</code> como essa aqui do lado, e ela ficar chamando ela mesma. Além disso, nessa função <code>f3</code> vou ficar passando o argumento hello toda vez. Cada chamada a <code>f3</code> vai duplicar a string de hello world na stack. Daí vamos imprimir o endereço que cada uma delas tá ocupando na stack.</p>
<p>Se compilar e executar notem como os endereços seguem uma sequência decrescente um atrás do outro. Não são números aleatórios. Como eu disse, a stack começa nos endereços mais altos, depois do espaço da kernel na memória. À medida que vamos empilhando novas chamadas de f3 e duplicando a variável hello como argumento, os endereços vão diminuindo. Ela vai do fim em direção ao meio da memória.</p>
<p>E no final, boom, o programa crasheia porque acabou a stack. A função <code>f3</code> fica chamando ela mesma infinitamente até bater no limite que o sistema operacional permite. No caso do linux dá um segmentation fault. Esse erro é bem genérico mas significa que você tentou acessar memória que não existe, ou que não te pertence, ou qualquer tipo de acesso a memória que o sistema não sabe como lidar. No nosso caso, por ter acabado a memória da stack. Se pegarmos o primeiro endereço impresso na tela e o último e subtrair um pelo outro, podemos saber quanto de memória da stack foi ocupada antes do crash.</p>
<p>Como expliquei antes, esse primeiro número é negativo porque em binário começa com o primeiro bit mais significativo sendo 1, que o <code>printf</code> entendeu como integer com sinal negativo. Mas se convertermos de volta pra hexa temos esse linguição aqui, hexa F nove sete D dois zero A oito. Mesma coisa fazemos com o último endereço que foi impresso na tela e vamos ter hexa quatro um zero nove três sete zero oito. Fazendo um menos o outro temos esse numerozão, que se formos dividindo por mil e vinte e quatro, vai dar exatamente 2 gigabytes. Então podemos estabelecer que nosso stack durou até bater 2 gigabytes.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>-109240152 = 0xF97D20A8 = 11111001 01111101 00100000 10101000
1091122952 = 0x41093708 = 01000001 00001001 00110111 00001000</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Recursão é extremamente importante na programação e você precisa garantir que uma hora essa recursão vai parar antes da stack encher. Existe um caso especial de recursão que não enche a stack agressivamente desse jeito e ela simplesmente vai fazer jump de volta pra algumas instruções pra trás sem ficar enchendo a stack a cada chamada recursiva. Isso se chama Tail Recursion e vou deixar de lição de casa pra vocês estudarem caso nunca tenham ouvido falar disso. A maioria das boas linguagens suporta tail recursion e isso diminui bastante os casos de estourar a stack.</p>
<p>Mas além da recursão temos outro problema nesse exemplo besta. Toda vez que chamamos a função <code>f3</code> recursiva, passamos a string de hello world e ela é passada por valor, ou seja, é duplicada. Cada chamada recursiva que fazemos, no mínimo, tá empilhando o endereço pra onde o return vai dar jump de volta, que é um endereço de 64-bits ou 4 bytes, “mais” a duplicata de hello world que custa 12 bytes. Só isso são no mínimo 16 bytes a cada chamada.</p>
<p>Ou seja, dos 2 gigabytes de stack, talvez uns 3 quartos tão ocupados só com duplicatas de hello world, que é um grande desperdício. Uma string idealmente deveria ser imutável. Quando precisamos modificar a String ou concatenar mais texto nela, aí sim precisaríamos fazer uma duplicata. Mas se a variável vai permanecer sempre a mesma como uma constante, não precisaríamos ficar duplicando toda vez que passamos pra outra função. Imagina se em vez de hello world essa string fosse um texto enorme, uma ou mais páginas da Wikipedia, ocupando 1 mega, ou talvez 1 giga. Em duas chamadas de função duplicando a variável passando por valor ia acabar a stack. Então o que fazemos?</p>
<p>Vamos nos lembrar que eu falei que a stack é só um dos segmentos da memória total. A memória virtual total de 64-bits caberia até 16 exabytes de dados. Meros 1 giga não é nada. Eu poderia duplicar um texto de 1 gigabyte trilhões de vezes até acabar a memória toda. Mas no nosso caso o segmento da stack acaba em 2 gigabytes. Então como fazemos?</p>
<p>Agora vem a parte mais complicada. Vou tentar explicar pra vocês o temido Pointer ou apontador. Parece difícil mas o básico é bem simples, na real. Em vez de alocar espaço na stack pra guardar meus strings ou mesmo qualquer outro array gigante, vamos alocar ela no heap. Já expliquei antes que o Heap é o segmento da memória total que começa nos endereços mais baixos perto de zero e cujos endereços vão incrementando.</p>
<p>Repetindo, enquanto os endereços da stack começam do endereço mais alto e vão decrementando em direção ao começo, os da heap começam no endereço mais baixo e vão incrementando em direção ao fim. O sistema operacional vai garantir que um não cruze os limites do outro no meio. Tecnicamente os endereços da heap podem ir incrementando até o limite de não ultrapassar os endereços do stack, entenderam?</p>
<p>Quando você instancia um objeto numa linguagem como Python, Javascript, Ruby, Java, C#, o bloco de memória que contém todos os dados do objeto ficam no heap. E na stack fica só endereço, ou a referência que aponta pra esse objeto, que numa arquitetura de 64-bits vai ter sempre o tamanho fixo de 4 bytes, independente de quanta memória foi alocada pro objeto no heap. O truque é simples, vamos voltar ao hello world e em vez de alocar na stack, vamos mover pro heap.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

void main() {

    char hello[] = &#34;Hello World&#34;;
    printf(&#34;from main: %d\n&#34;, &amp;hello);

    char *hello2 = malloc(sizeof(hello));
    strcpy(hello2, hello);
    printf(&#34;hello2: %x\n&#34;, hello2);

    char *hello3 = hello2 &#43; 6;
    printf(&#34;from hello2: %s\n&#34;, hello2);
    printf(&#34;from hello3: %s\n&#34;, hello3);

    return;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Em C precisamos incluir o cabeçalho <code>stdlib.h</code> que nos dá acesso à famosa função <code>malloc</code> que significa “memory allocation”. O argumento que passamos é quantos bytes queremos que ela reserve no heap. No caso, podemos usar outra função chamada <code>sizeof</code> que, como o nome diz, mede o tamanho de alguma coisa. E como parâmetro pra essa função passamos a variável hello que alocamos na stack, que é 12 bytes.</p>
<p>Poderíamos passar direto o número 12, também. Mas o que o <code>malloc</code> vai fazer é procurar um segmento contínuo de 12 bytes no heap e passar pra gente o endereço que ele reservou no heap. E essa nova variável <code>hello2</code> vai guardar esse endereço, que é um número de 4 bytes. Então esse <code>hello2</code> é um ponteiro porque seu endereço aponta aonde vai estar o string na Heap. É só isso.</p>
<p>Aqui a coisa pode ficar mais cabeluda ainda, então prestem atenção. Com esse endereço em mãos podemos usar outra função do C, o <code>strcpy</code> ou string copy, passando como argumentos esse endereço e a variável hello antiga pra copiar o hello world pra esse novo espaço na heap. Essa é a única duplicata que vamos ter a partir de agora. A parte importante é entender que na variável <code>hello2</code> nós não temos a string hello world e sim o endereço pro primeiro byte na heap onde vamos encontrar essa cópia do hello world.</p>
<p>Sendo o hello2 um ponteiro com um endereço e sabendo que cada char da string tem exatamente um byte e sabendo que ela acaba quando encontramos o caracter NULO, se fizermos outro ponteiro chamado <code>hello3</code> que é <code>hello2</code> mais 6, agora vamos ter só a palavra <code>World</code> se dermos <code>printf</code> nela. Vamos compilar e rodar pra ver. Olha como imprime na tela o Hello world do ponteiro hello2 e só world do ponteiro hello3. Mas a gente não criou ou duplicou uma nova string, estamos apontando pro mesmo espaço na memória só que 6 bytes mais pra frente.</p>
<p>Essa sintaxe ou notação de asterisco é pra indicar à linguagem que essa variável é um ponteiro e queremos acessar o valor gravado no heap, nesse exato endereço. Se não colocarmos o asterisco, vemos direto só o endereço. Se colocarmos o asterismo ele trás o valor pra onde esse endereço tá apontando. Tanto as variáveis <code>hello2</code> quanto <code>hello3</code> parecem representar um string diferente mas na verdade são só endereços ou, mais corretamente, referências, ao mesmo string, só apontando pra posições diferentes no mesmo string. Pra acessar o string em si, usamos asterisco hello2 e asterisco hello3.</p>
<p>Seja um string ou seja um array, já que string é um caso especial de array, a variável <code>hello</code> não contém &ldquo;O&rdquo; array e sim o endereço pro primeiro elemento do array. Se eu quiser pegar um elemento no meio do array, primeiro pego o endereço e somo a quantidade de bytes que leva ao elemento que eu quero. Se for um array de chars, que é uma string, onde cada char tem 1 byte; pra pegar a 6a letra, basta somar 6 bytes ao primeiro endereço.</p>
<p>Quando fazemos aquela notação que tem em toda linguagem, com brackets ou colchetes, com um número no meio, a linguagem tá traduzindo por baixo em uma soma de endereço, como eu mostro aqui no código. É o endereço inicial, mais o tamanho de cada elemento multiplicado pela posição. Vejam que estou repetindo essa operação pra tentar deixar bem claro esse funcionamento.</p>
<p>Tem uma última estrutura de dados que eu quero explicar pra vocês hoje. Como eu repeti de propósito várias vezes até agora, arrays são listas que tem sempre os mesmos tipos de elementos, ou mais corretamente, que tem elementos de mesmo tamanho. Se for um string, é um array onde cada elemento tem 1 byte, se for um array de inteiros de 64-bits, todo elemento tem exatamente 4 bytes. E eu vim repetindo a mesma coisa sobre elementos iguais por causa do seguinte: e se eu quiser uma sequência onde cada elemento tem tamanhos diferentes?</p>
<p>Pra isso, em linguagens de alto nível tipo Python existe a tupla ou tuple, ou no caso de C existe Struct. Em conceito não são a mesma coisa, mas pra hoje pense que é. Vou explicar porque. Diferente de um array onde cada elemento em sequência tem o mesmo tamanho, num struct ou tupla podemos declarar tipos diferentes que vão ser concatenados um atrás do outro no linguição de bits.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;inttypes.h&gt;

struct Person {
    char nome[10];
    uint8_t age;
    uint8_t height;
};

void main() {
    struct Person person;
    strcpy(person.nome, &#34;Fabio&#34;);
    person.age = 43;
    person.height = 172;

    printf(&#34;%x\n&#34;, &amp;person);

    return;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Por exemplo, podemos declarar uma Struct ou estrutura chamada Person, cujo primeiro elemento vai ser um array de chars de 10 bytes, um nome curtinho. O segundo elemento vai ser um int de 8 bits, ou 1 byte, chamado age, pra ser a idade dessa pessoa. E finalmente o terceiro elemento vai ser a altura ou height, que vai ser um inteiro de 8 bits também.</p>
<p>Essa estrutura define um tipo de dados novo, chamado Person, cujo tamanho total sempre vai ser de 12 bytes. Agora eu posso criar uma variável que vai ser esse tipo e o C vai preencher os valores que vamos passar na sequência correta na fita de bits. Se eu fizer <code>person.age</code> igual a 43, <code>person.height</code> igual a 172 e <code>person.name</code> igual a &ldquo;Fabio&rdquo;, a cadeia de bits vai ficar exatamente assim em hexadecimal. Lembrando que cada dois dígitos em hexa representa 1 byte, começamos com o hexa quatro seis que é a letra &ldquo;F&rdquo;, daí 0x61 que é &ldquo;a&rdquo; daí chega uma sequência de zeros porque &ldquo;Fabio&rdquo; tem menos de 10 bytes.</p>
<p><code>46 61 62 69 6f 00 00 00 00 00 2B AC</code></p>
<p>O penúltimo hexa 2B é a idade 43, e o último hexa AC é 172. A Struct meio que serve como um molde, que encaixa exatamente nessa sequência de bits e quebra os valores pra dentro dos campos que criamos. No caso de idade e altura, como são inteiros de 8 bits sem sinal, sabemos que podemos ter idades de zero até duzentos e cinquenta e cinco e alturas de no máximo duzentos e cinquenta e cinco centímetros, e tirando alguma raríssima exceção, tipo se você for mais alto que o Hulk, isso cobre qualquer pessoa do mundo.</p>
<p>Única coisa nesse trecho de código que talvez deixe você confuso é porque podemos fazer <code>person.age</code> igual 43 mas não podemos fazer <code>person.name</code> igual &ldquo;Fabio&rdquo; e em vez disso eu usei a função que copia strings de duas posições da memória, que é essa <code>strcpy</code>. Só entenda que é assim que se faz em C, mas em outras linguagens como Javascript você faz do jeito mais simples de só usar &ldquo;igual&rdquo;. Coisas desse tipo que linguagens de mais alto nível facilitam. É o que eu chamaria de ergonomia da linguagem.</p>
<p>Mas com o que eu expliquei até agora, você conhece os principais tipos numéricos primitivos como integers e floats, arrays e strings, e agora structs. Um Tuple é como se fosse um Struct anônimo, sem nomes e etiquetas, parecido com um array. Aliás, toda vez que você ver um Tuple num Python da vida, vai achar que é a mesma coisa que um array. Uma Tupla é um conjunto imutável com elementos de tipos diferentes, só isso. Um Array é um conjunto mutável de elementos do mesmo tipo. Pra extrair os elementos de uma tupla, você precisa de um molde que declara qual o tamanho de cada elemento, um molde que vai ser parecido com um Struct.</p>
<p>Estamos chegando no final, e agora com tudo que aprendemos, só falta mais um truque pra vocês começarem a entender melhor como os programas funcionam. Vocês meio que já devem ter entendido que funções nada mais são que sequências de instruções que ficam localizados em algum endereço na memória. A CPU vai executando “linha a linha” e quem dita qual linha é o tal contador de programas. Daí quando uma função chama outra, ela primeiro faz bookmark desse contador na stack, e dá JUMP pro endereço dessa outra função.</p>
<p>Repetindo, é como se cada uma dessas linhas de código ficasse num endereço na memória. Estamos simulando como se fossem as linhas do código no editor de textos. Quando na função <code>main</code> lá embaixo eu chamo a função <code>f1</code>, e o compilador do C gera o binário, ele vai substituir por algo semelhante a um <code>jump</code> ou mesmo <code>goto</code> em linguagens mais antigas, pro endereço da linha da função <code>f1</code>.  No comentário é um pseudo-código de como seria se o C tivesse uma função chamada <code>jump</code>.</p>
<p>E isso é uma das razões de porque precisamos de compiladores. Ia ser chato pra caramba ter que ficar lembrando o endereço de cada função que precisamos. E aí a gente resolve mexer na função, muda de lugar, e isso muda o endereço, daí tem que mudar todo lugar que chama esse endereço na mão. Puta trampo. Em vez disso usamos nomes que representam esses endereços, como nomes de função e nomes de variáveis, e deixamos o compilador traduzir isso em endereços no binário final.</p>
<p>Isso é outro conceito importante: nomes de coisas não precisam existir no binário. Se a gente quiser podemos dizer pro compilador manter os símbolos pra debug. Símbolos são esses nomes. Os campos nome, idade e altura não existem no binário onde estão os valores. É só um linguição de bits e só. Linguagens de mais alto nível às vezes guardam esses símbolos por padrão. São metadados ou dados que explicam os dados, e metadados ocupam espaço.</p>
<p>Mas como não são necessários pra rodar o programa e sim pra debugar depois, não é bom guardar tudo porque seria um desperdício de espaço. Se você já mexeu com coisas como XML ou JSON, eles são grandes e desperdiçam bastante espaço, primeiro porque representam tudo como strings e segundo porque levam os metadados de tudo junto com os dados. Mas isso é um detalhe que vou explorar em outro episódio. Aliás, é por isso que um Google inventou coisas como Protobufs, se você estiver interessado em mais um assunto pra estudar.</p>
<p>No caso do texto do código, nós declaramos símbolos, ou nomes pra endereços e cadeias de bits, pra facilitar a vida do programador de ler o código ou debugar depois, mas a máquina em si não se importa com nada disso. Pro computador é tudo uma cadeia de bits e só. O computador não dá a mínima pra como você chama as coisas ou o significado que dá pra elas. Um código é feito pra outros programadores entenderem. Computadores só entendem os binários sem nomes e sem significados. Entenda esse conceito.</p>
<p>A parte importante é que tudo tem endereços, não só variáveis mas funções também. Em particular, em C, podemos pegar a referência de uma função e passar como argumento pra outra função, da mesma forma como eu passei o endereço da variável <code>hello</code> pro <code>printf</code> imprimir na tela, lembra? Por exemplo, vamos reorganizar esse código de criar struct numa função nova chamada <code>createPerson</code> onde passamos como argumentos o nome, idade, altura e um último argumento que é um ponteiro pra uma função que não retorna nada, então <code>void</code> mas tem como argumento um struct de Person.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;inttypes.h&gt;

struct Person {
    char nome[10];
    uint8_t age;
    uint8_t height;
};

void createPerson(char name[],
  uint8_t age,
  uint8_t height,
  void(*function_pointer)(struct Person)) {
    struct Person person;
    strcpy(person.nome, &#34;Fabio&#34;);
    person.age = 43;
    person.height = 172;

    (*function_pointer)(person);
  }

void printPerson(struct Person person) {
    printf(&#34;Person: %s %d %d\n&#34;,
        person.nome, person.age, person.height);
}

void printPerson2(struct Person person) {
    printf(&#34;nome: %s age: %d height: %d\n&#34;,
        person.nome, person.age, person.height);
}

void main() {
    createPerson(&#34;Fabio&#34;, 43, 172, &amp;printPerson);
    createPerson(&#34;Fabio&#34;, 43, 172, &amp;printPerson2);
    return;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dentro dela fazemos igualzinho antes, onde declaramos a struct Person, preenchemos os campos mas no final executamos a função que veio nesse endereço chamado <code>function_pointer</code>. Entenda isso. a variável <code>function_pointer</code>, assim como aquela <code>hello2</code> vai ser só uma referência, um endereço pra alguma função que tem as características que declaramos, que retorna <code>void</code> ou seja, nada, e tem uma struct Person como argumento. Ao colocar um asterisco na frente dela, é como se estivéssemos dando copy e paste do nome da função de verdade aqui e executando ela.</p>
<p>A seguir vamos declarar a função pra passar pra esse <code>createPerson</code>. Vamos chamar de <code>printPerson</code>, que retorna <code>void</code> e recebe um <code>Person</code> de argumento e dentro é só um <code>printf</code> como fizemos antes. Mas note que diferente de antes, onde precisamos declarar acima as funções que vamos usar embaixo, estamos declarando o <code>printPerson</code> abaixo da função <code>createPerson</code> porque ela ainda não sabe que é essa função que vamos usar.</p>
<p>Ela só sabe que vai receber uma referência que pode ser pra qualquer outra função. Se a <code>createPerson</code> chamasse direto a <code>printPerson</code> ela precisaria ter sido declarada antes. Mas vamos ver a seguir como eu passo a referência dessa função pra <code>createPerson</code>.</p>
<p>Na função <code>main</code> chamamos a <code>createPerson</code> passando os mesmos valores de antes, &ldquo;Fabio&rdquo;, 43 e 172 e em seguida &amp; comercial e <code>printPerson</code>. Aqui estamos pegando a referência, o endereço da função e passando como argumento. Quando compilamos e executamos vemos que a saída na tela é igual antes.</p>
<p>Esse &amp; comercial <code>printPerson</code> tá pegando o endereço onde a função tá declarada e passando como argumento pra <code>createPerson</code>. É só um integer que faz cast pra uma função. Cast é a ação de declarar que tipo esse número representa pro compilador do C saber o que fazer com ela. Mas só isso não tem graça, vamos declarar uma segunda função <code>printPerson2</code> pra ver como podemos passar uma referência diferente.</p>
<p>Lá em cima, fazemos uma cópia do <code>printPerson</code>, chamamos de <code>printPerson2</code> e vamos só mudar o jeito que o <code>printf</code> mostra os dados. De volta lá embaixo vamos chamar o <code>createPerson</code> de novo, mas passando a referência pra esse novo <code>printPerson2</code>. Depois de compilar e executar, veja que imprime essa nova versão também.</p>
<p>A função <code>createPerson</code> aceita qualquer função compatível, que tenha a mesma assinatura, ou seja, o tipo de retorno e os tipos de argumentos que ele espera. Isso é o que chamamos de assinatura de uma função. Ela só recebe a referência pra função que estiver nesse endereço. Basta essa função ter a assinatura correta, ou seja, aceitar a struct de Person e retornar void, ou nada.</p>
<p>Isso de uma função ser passada como referência pra outra função é o que você já deve ter ouvido falar como um <code>callback</code>. É uma função que vai ser chamada &ldquo;call&rdquo; de volta &ldquo;back&rdquo;. Qualquer um que tenha se interessado por programação funcional sabe que uma característica importante é ter funções que recebem outras funções como argumento. A função <code>createPerson</code> é o que em funcional o povo chama de &ldquo;High Order Function&rdquo;.</p>
<p>Quando em Javascript, Python ou Ruby você usa funções como <code>forEach</code>, <code>map</code>, <code>filter</code>, <code>reduce</code> e outros, eles recebem como argumento a referência pra outra função, normalmente uma função anônima, ou seja, que não tem nome e é declarada dentro dos parênteses de argumento. E com isso você entendeu o primeiro passo pra ter uma linguagem funcional.</p>
<p>Na teoria, sim, você poderia escrever código C do jeito “funcional”, mas ninguém faz isso porque não é prático. Mas com isso você pode imaginar como C poderia ser usado pra criar uma linguagem funcional como um Lisp da vida. É assim que a gente começa a manipular uma linguagem pra aceitar paradigmas que ela não foi projetada pra fazer. E com o que aprendemos até agora eu já posso mostrar mais um passo adiante.</p>
<p>Vamos entender o primeiro passo que leva C pra uma linguagem orientada a objetos como C++ ou Objective-C. O que eu vou mostrar a seguir não é a implementação da verdade, mas só um rascunho de como poderia ser. Quando eu penso em orientação a objetos só penso em duas coisas: um ponteiro pra uma struct e ponteiros pra funções cujo primeiro argumento, o que você aprendeu num Python ou Javascript como <code>self</code> ou <code>this</code>, sendo um ponteiro de volta pra mesma struct.</p>
<p>Vamos refatorar a <code>createPerson</code> primeiro. Até agora ela tá declarando a struct na stack e quando chama a <code>printPerson</code> tá duplicando por causa da passagem por valor. Em vez disso, vamos criar a struct no Heap e economizar espaço no stack fazendo passagem por referência. Pra isso precisamos usar <code>malloc</code> passando o tamanho da struct como parâmetro. E isso pra mim é o que significa <code>instanciar um objeto na memória</code>. Basicamente alocar espaço na heap pra uma struct.</p>
<p>No caso de C, quando criamos qualquer coisa no Heap, precisamos colocar asterisco na frente da variável que guarda essa referência pra acessar o valor, mas no caso de struct em C basta trocar essa notação de &ldquo;ponto&rdquo; pra uma seta com tracinho e sinal de maior. Se você é de C++ ou PHP já viu essa notação e eu acho que ela deriva disso em C mesmo. Pra acessar os campos de uma struct de C que foi alocada na heap, sem precisar usar parênteses e ponto, basta usar a setinha, é a mesma coisa.</p>
<p>Agora vamos tirar o argumento de ponteiro de função do final do <code>createPerson</code>. Em vez disso, vamos mover pra struct de Person. Um novo campo que é um ponteiro de função chamado <code>show</code>. Dentro da <code>createPerson</code> declaramos que o campo <code>show</code> desse person que instanciamos no heap vai apontar pra referência da função <code>printPerson</code> que a gente já tinha. Finalmente, vamos mudar o tipo de retorno da função pra devolver o ponteiro dessa struct e no final podemos dar return do person que acabamos de criar e configurar.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;inttypes.h&gt;
#define Class struct

Class Person {
    char nome[10];
    uint8_t age;
    uint8_t height;
    void(*show)(Class Person *);
};

void person_print(Class Person *self) {
    printf(&#34;nome: %s age: %d height: %d\n&#34;,
        self-&gt;nome, self-&gt;age, self-&gt;height);
}

Class Person * newPerson(char name[],
  uint8_t age,
  uint8_t height) {
    Class Person *self =
      (Class Person *) malloc(sizeof(Class Person));
    strcpy(self-&gt;nome, &#34;Fabio&#34;);
    self-&gt;age = 43;
    self-&gt;height = 172;

    self-&gt;show = &amp;person_print;
    return self;
  }

void main() {
    Class Person *person2 = 
      (Class Person *) newPerson(&#34;Fabio&#34;, 43, 172);

    person2-&gt;show(person2);

    return;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quem já programa orientado a objetos em alguma linguagem pode estar achando isso familiar. Eu chamaria essa função <code>createPerson</code> de “construtor”. Um constructor é uma função responsável por alocar espaço pra um objeto na memória Heap e configurar os valores iniciais desse objeto, retornando o endereço, ou referência, pra esse objeto.</p>
<p>Agora vamos usar isso na função <code>main</code>. Primeiro vamos armazenar o ponteiro que a <code>createPerson</code> vai criar numa variável que é um ponteiro, um asterisco person. E passamos os valores que queremos como argumento a esse construtor. E pra imprimir esses valores na tela podemos chamar direto o método deste “objeto”. E pra quem não sabe, “método” é o nome que damos a funções associadas a um objeto. O método <code>show</code> já foi configurado no construtor pra ser um ponteiro pra <code>printPerson</code>.</p>
<p>A última refatoração que precisamos fazer é mudar a função <code>printPerson</code> pra usar essa variável <code>self</code> pra acessar os campos de nome, idade e altura. Se você é de Python, deve estar começando a se sentir em casa porque métodos de objetos de Python tem explicitamente o primeiro argumento recebendo <code>self</code>. E é o que estou simulando aqui em C.</p>
<p>Em Javascript e outras linguagens orientadas a objetos, em vez de <code>self</code> chamamos de <code>this</code> mas é mais ou menos a mesma coisa, só que o primeiro argumento é implícito, você não precisa declarar ela. Mas faça de conta que a linguagem tá botando ela lá pra você. Então, só chamando <code>person</code> setinha <code>print</code> e passando <code>person</code> como o argumento <code>self</code> vai tudo funcionar. Qual seria um próximo passo pra ficar mais parecido com uma linguagem orientada a objetos?</p>
<p>Pra quem não sabe, em C tem uma coisa chamada <code>pré-processamento</code>, que é reescrever o código antes de compilar. Se você é de Javascript pense como um “transpiler”. Por exemplo, lá no topo do arquivo podemos declarar uma cerquilha <code>#DEFINE</code> e dizer que toda vez que achar escrito <code>Class</code> pode trocar pra <code>struct</code> antes de compilar. Onde tá <code>struct Person</code> podemos substituir como <code>Class Person</code>. Vamos lá usar a função de procurar e substituir do editor e boom. Olha só como de repente parece que estamos lidando com uma linguagem orientada a objetos de verdade.</p>
<p>Se você aguentou até aqui, espero que tenha conseguido minimamente visualizar o que são os diferentes tipos de dados, as diferenças entre inteiros e floats, arrays,  tuples e structs. Como dados são alocados na stack que é a pilha e como a execução de um programa com funções que chamam outras funções vai sendo empilhado e desempilhado da stack. Como tudo tem endereços ou referências, incluindo funções.</p>
<p>Algumas linguagens expõem referências a funções, mas não permitem manipular essas referências pra apontar pra outras regiões de memória. Javascript é assim. Um Java lá atrás só tinha referência a objetos, por isso a única forma de passar funções pra outras funções era primeiro encapsular numa classe anônima, instanciar e passar a referência do objeto como argumento pra outras funções. Por baixo dos panos é assim que as coisas funcionam.</p>
<p>Pra complementar, recomendo rever os vídeos sobre gerenciamento de memória. Quando fazemos programinhas simples, que alocam poucos dados e não tem nenhuma recursão maluca, o stack é mais que suficiente. E você não precisa lidar com o heap. O problema de alocar coisas no heap é que quando termina a função que fez a alocação, o ponteiro sai do stack, mas o que foi alocado no heap continua lá ocupando espaço se você não der <code>free</code> pra liberar explicitamente. Memória na heap não se desempilha sozinha. Por isso surge a necessidade de um garbage collector ou outros truques como o ARC de Swift pra limpar o heap.</p>
<p>Só que garbage collector não faz milagres. Ele sempre vai reservar mais memória do que realmente precisa e sempre vai causar pausas na execução do programa pra fazer essa limpeza de tempos em tempos. Mesmo se você tiver memória sobrando pra desperdiçar, essas pausas pra limpeza sempre vão ser um problema. E é o que linguagens como Swift ou Rust ou C tentam evitar. Por isso são melhores pra fazer coisas como sistemas operacionais, drivers ou coisas de mais baixo nível que vão ser eficientes no uso de recursos e evitar pausas de manutenção o máximo possível. O mesmo não se pode dizer de Java, C# ou Go.</p>
<p>Linguagens interpretadas como Javascript, Python, Ruby, PHP são extensões bem pesadas em cima de C. Elas adicionam diversas funcionalidades pra facilitar a programação, mas por baixo dos panos o que faz elas funcionarem mais rápido e mais eficiente são bibliotecas escritas em C. Uma biblioteca escrita só em Python ou Javascript nunca vai ser nem tão rápida nem tão eficiente. Por isso, funções como criptografia, funções de I/O pra lidar com arquivos ou pacotes de rede, funções matemáticas e tudo mais é tudo escrito em C. Eu chamo elas de linguagens &ldquo;cola&rdquo;.</p>
<p>Você só escreve a cola em Python, que é uma sintaxe mais simples, pra mexer com funções mais complicadas que estão escritas em C ou C++. Por exemplo, em machine learning, Python não é importante porque as funções de um Tensorflow são todas escritas em C++. Se você usar SciPy pra computação científica, de novo as funções são todas em C++ ou Fortran. Nenhuma linguagem cola é rápida o suficiente pra essas coisas. São boas como cola, pra consumir essas coisas mais fácil.</p>
<p>Se você mexe em machine learning em Python, você é só um consumidor de ferramentas escritas em C++ ou C e faz cola em Python. Não tem nenhum problema nisso, e na prática a maioria de vocês assistindo nunca vai precisar de mais que isso. Linguagens que são &ldquo;cola&rdquo; nunca vão ser boas pra fazer o que eu chamo de &ldquo;sistemas&rdquo;. Coisas como drivers, criptografia, processamento massivo de dados como um banco de dados ou mesmo protocolos de rede eficientes.</p>
<p>Elas sempre vão ser só a cola pra sistemas feitos em C++ ou C. Por isso eu recomendo que você saiba no mínimo uma linguagem de mais baixo nível como C ou Rust. E junto uma linguagem cola pra ter produtividade como Javascript ou Python. Quem só sabe linguagens &ldquo;cola&rdquo; sempre vai achar que o que acontece no baixo nível é &ldquo;mágica&rdquo;.</p>
<p>E com o episódio de hoje eu espero ter conseguido quebrar um pouco dessa &ldquo;mágica&rdquo; e fazer vocês enxergarem suas linguagens favoritas com outros olhos. Claro, tudo que eu falei rápido em um episódio é o que se ensina em no mínimo um semestre em ciências da computação, talvez um ano ou até mais.</p>
<p>Eu só mencionei estruturas de dados simples, não falei de listas ligadas, árvores, red black, AVL e nem cheguei a falar dos algoritmos pra lidar com essas estruturas. Quem sabe em outro episódio no futuro, mas entenda que tudo que eu falei hoje é o básico em ciências da computação.</p>
<p>Se você se considera programador e nunca viu nada do que expliquei hoje, considere estudar e treinar mais. Isso vai te ajudar a subir pro próximo nível na programação. Se ficaram com dúvidas mande nos comentários abaixo, inclusive se já sabem de tudo que eu disse aqui, ajudem a responder às dúvidas dos colegas nos comentários também. Não deixem de assinar o canal e clicar no sininho pra não perder os próximos episódios. Compartilhem o video pra ajudar o canal. A gente se vê, até mais!</p>
<p>Links:</p>
<ul>
<li>Integer (Wikipedia) (<a href="https://en.wikipedia.org/wiki/Integer_%28computer_science%29"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Integer_(computer_science)</a>)</li>
<li>Two’s Complement (Wikipedia) (<a href="https://en.wikipedia.org/wiki/Two%27s_complement"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Two%27s_complement</a>)</li>
<li>How numbers are encoded in JavaScript (<a href="https://2ality.com/2012/04/number-encoding.html#:~:text=JavaScript%20numbers%20are%20all%20floating,binary%20format%2C%20in%2064%20bits"target="_blank" rel="noopener">https://2ality.com/2012/04/number-encoding.html#:~:text=JavaScript%20numbers%20are%20all%20floating,binary%20format%2C%20in%2064%20bits</a>.)</li>
<li>FLOATING POINT VISUALLY EXPLAINED (<a href="https://fabiensanglard.net/floating_point_visually_explained/"target="_blank" rel="noopener">https://fabiensanglard.net/floating_point_visually_explained/</a>)</li>
<li>What Every Computer Scientist Should Know About Floating-Point Arithmetic (What Every Computer Scientist Should Know About Floating-Point Arithmetic (oracle.com))</li>
<li>IEEE-754 Floating Point Converter (IEEE-754 Floating Point Converter (h-schmidt.net))</li>
<li>Number.MAX_SAFE_INTEGER (Number.MAX_SAFE_INTEGER - JavaScript | MDN (mozilla.org))</li>
<li>Signed Binary/Decimal Conversion Using the Two&rsquo;s Complement Representation (Signed Binary/Decimal Conversion (ubc.ca))</li>
<li>C - Pointer arithmetic (C - Pointer arithmetic - Tutorialspoint)</li>
<li>Why Discord is switching from Go to Rust (<a href="https://blog.discord.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f"target="_blank" rel="noopener">https://blog.discord.com/why-discord-is-switching-from-go-to-rust-a190bbca2b1f</a>)</li>
</ul>
]]></content:encoded><category>linguagens-de-programacao</category><category>ciencia-da-computacao</category><category>programacao</category><category>akitando</category></item><item><title>[Akitando] #46 - Gerenciamento de Memória (Parte 2) | Entendendo Back-end para Iniciantes (Parte 6)</title><link>https://akitaonrails.github.io/2019/04/03/akitando-46-gerenciamento-de-memoria-parte-2-entendendo-back-end-para-iniciantes-parte-6/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/04/03/akitando-46-gerenciamento-de-memoria-parte-2-entendendo-back-end-para-iniciantes-parte-6/</guid><pubDate>Wed, 03 Apr 2019 17:01:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/DGU1awKrNiA"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Finalmente, chegamos no último episódio do tema de Back-End!&lt;/p&gt;
&lt;p&gt;Devo dizer que este foi um dos episódios que eu mais queria explicar. Toda nova linguagem hoje em dia tem Garbage Collector. Mas a maioria dos programadores não tem a mínima idéia de como eles funcionam.&lt;/p&gt;
&lt;p&gt;Mais importante: todos acreditam que garbage collectors são mágicos e &amp;ldquo;simplesmente funcionam&amp;rdquo; mas não entendem quais são os reais motivos de porque eles existem, quais problemas eles resolvem, e quanto eles CUSTAM pro seu programa. Sim! Nenhuma mágica vem de graça.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/DGU1awKrNiA"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Finalmente, chegamos no último episódio do tema de Back-End!</p>
<p>Devo dizer que este foi um dos episódios que eu mais queria explicar. Toda nova linguagem hoje em dia tem Garbage Collector. Mas a maioria dos programadores não tem a mínima idéia de como eles funcionam.</p>
<p>Mais importante: todos acreditam que garbage collectors são mágicos e &ldquo;simplesmente funcionam&rdquo; mas não entendem quais são os reais motivos de porque eles existem, quais problemas eles resolvem, e quanto eles CUSTAM pro seu programa. Sim! Nenhuma mágica vem de graça.</p>
<p>Hoje vamos usar o que aprendemos até agora pra finalmente olhar linguagens como Objective-C/Swift, Python, Ruby, Java, Erlang/Elixir, Go e entender como eles diferem no gerenciamento de memória.</p>
<p>Links:</p>
<ul>
<li>Sun Java System Application Server 9.1 Performance Tuning Guide (<a href="https://docs.oracle.com/cd/E19159-01/819-3681/abeio/index.html"target="_blank" rel="noopener">https://docs.oracle.com/cd/E19159-01/819-3681/abeio/index.html</a>)</li>
<li>Garbage collection in Python: things you need to know (<a href="https://rushter.com/blog/python-garbage-collector/"target="_blank" rel="noopener">https://rushter.com/blog/python-garbage-collector/</a>)</li>
<li>What causes Ruby memory bloat? (<a href="https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html"target="_blank" rel="noopener">https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html</a>)</li>
<li>Demystifying the Ruby GC (<a href="https://samsaffron.com/archive/2013/11/22/demystifying-the-ruby-gc"target="_blank" rel="noopener">https://samsaffron.com/archive/2013/11/22/demystifying-the-ruby-gc</a>)</li>
<li>Erlang Garbage Collector (<a href="https://www.erlang-solutions.com/blog/erlang-garbage-collector.html"target="_blank" rel="noopener">https://www.erlang-solutions.com/blog/erlang-garbage-collector.html</a>)</li>
<li>Go, don&rsquo;t collect my garbage (<a href="https://blog.cloudflare.com/go-dont-collect-my-garbage/"target="_blank" rel="noopener">https://blog.cloudflare.com/go-dont-collect-my-garbage/</a>)</li>
<li>Tuning glibc Memory Behavior (<a href="https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior"target="_blank" rel="noopener">https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior</a>)</li>
<li>The status of Ruby memory trimming &amp; how you can help with testing (<a href="https://www.joyfulbikeshedding.com/blog/2019-03-29-the-status-of-ruby-memory-trimming-and-how-you-can-help-with-testing.html"target="_blank" rel="noopener">https://www.joyfulbikeshedding.com/blog/2019-03-29-the-status-of-ruby-memory-trimming-and-how-you-can-help-with-testing.html</a>)</li>
<li>HOW THREE GUYS REBUILT THE FOUNDATION OF FACEBOOK (<a href="https://www.wired.com/2013/06/facebook-hhvm-saga/"target="_blank" rel="noopener">https://www.wired.com/2013/06/facebook-hhvm-saga/</a>)</li>
<li>Ruby Memory Environment Variables - Simpler Than They Look. (<a href="http://engineering.appfolio.com/appfolio-engineering/2018/6/27/ruby-memory-environment-variables-simpler-than-they-look"target="_blank" rel="noopener">http://engineering.appfolio.com/appfolio-engineering/2018/6/27/ruby-memory-environment-variables-simpler-than-they-look</a>)</li>
<li>&ldquo;WEAK, STRONG, UNOWNED, OH MY!&rdquo; - A GUIDE TO REFERENCES IN SWIFT (<a href="https://krakendev.io/blog/weak-and-unowned-references-in-swift"target="_blank" rel="noopener">https://krakendev.io/blog/weak-and-unowned-references-in-swift</a>)</li>
</ul>
<p>Olá pessoal, Fabio Akita</p>
<p>Este é o décimo primeiro episódio da série começando aos 40, parte 2 do tema de Gerenciamento de Memória, parte 6 e conclusão do tema de Back-end, finalmente!! Semana passada eu dei uma escovada de bits, expliquei um pouco sobre as diferenças de arquiteturas 64-bits e 32-bits, entendemos rapidamente sobre notação binária e hexadecimal, os diferentes tipos de memória e como os diferentes alocadores fazem seu trabalho pra resolver os problemas de fragmentação e alocação eficiente em ambientes com múltiplas threads reais.</p>
<p>No episódio de hoje vamos uma camada pra cima e explorar mais sobre os famosos garbage collectors, que muita gente ainda associa mais com Java, mas que a maioria das linguagens interpretadas e baseadas em máquinas virtuais possuem. O grande truque é balancear processamento e uso de memória. Se ficar tentando economizar memória, o collector precisa trabalhar mais e, por consequência isso diminui a performance geral da sua aplicação. Por outro lado, se o collector rodar pouco pra aumentar a performance geral, você vai acabar usando mais memória do sistema e em ambientes de servidor onde os processos ficam de pé por dias ou semanas sem descarregar, isso pode ser um problema também. Então, como eles fazem pra balancear os dois?</p>
<p>(&hellip;)</p>
<p>Recapitulando, no episódio anterior falamos um pouco das diferenças de alocadores como o ptmalloc2, tcmalloc do Google e o jemalloc que o Facebook usa. No fim, todo alocador vai querer resolver o problema de fragmentação, o problema de contenção de threads, o problema de cache entre threads. E agora temos os garbage collectors. Pense em garbage collectors como sendo um alocador em user-land. Ele faz syscalls pro sistema operacional, como o malloc() pra pedir blocos grandes de memória e internamente gerencia como esse pedação vai ser usado. Python, Ruby, Erlang, Javascript, .NET, Java e por consequência toda linguagem que roda em cima de Java como Scala, Clojure, Kotlin, usam o mesmo garbage collector da JVM. Assim como schedulers the coroutines em user-land são mais rápidos como eu já expliquei em episódios anteriores, um alocador em user-land também é mais rápido do que fazer syscalls pra funções no kernel-space como mmap ou munmap que é o que funções como malloc() fazem.</p>
<p>Todo garbage collector faz algumas coisas meio básicas. Não dá pra explicar no detalhe como o collector de cada linguagem funciona, mas vamos ver os conceitos comuns. Pra começar, existem linguagens que lidam com ponteiros, ou seja os tais endereços virtuais diretamente, e os que lidam com referências que são como se fossem atalhos pros endereços. Garbage collectors existem principalmente porque nós programadores somos muito ruins em liberar memória manualmente.</p>
<p>Se você teve que lidar com C, sabe que precisa chamar a função free() pra toda memória que deu malloc() antes. Esqueça de dar free e seu programa vai começar a ter leaks ou vazamentos e vai ficar crescendo sem parar. Parece super simples, mas toda regrinha desse tipo você pode ter certeza que vai esquecer. Basta esquecer de digitar uma linha ou apagar uma linha do código com free por acidente. Ou pior, se você der free numa memória que ainda não era pra dar free, vai ficar com um Invalid Pointer por exemplo e seu programa pode crashear. Quando você adiciona classes e objetos em cima do C e gera um C++ a coisa fica pior ainda porque você vai passando ponteiros de objetos pra outros objetos numa árvore gigante de dependências. Quando que você pode destruir um objeto sem deixar outro que dependia dele corrompido?</p>
<p>Uma das formas de resolver esse problema é adicionar a todo objeto um contador. E toda vez que você passar esse objeto pra outro objeto ou função, incrementar o contador. Quanto mais objetos apontarem pra esse primeiro, maior vai ficando o contador. À medida que os objetos que apontam pro primeiro também forem sendo liberados, o contador vai decrementando. E quando o contador voltar a ser zero, daí você sabe que ninguém está mais usando e pode liberar esse objeto. Como estamos falando de contagem de referências, você vai ouvir falar nessa estratégia como Reference Counting. Linguagens como Python e Objective-C e, por consequência, Swift usam essa estratégia.</p>
<p>No caso do Objective-C ele vai um passo além, ele inclui uma classe chamada NSAutoreleasePool, um tanque de alocação. Você instancia um pool e à medida que vai alocando novos objetos esse pool fica sabendo deles. Diferente de um garbage collector, em Objective-C você manualmente instância quantos pools quiser, e tem que manualmente chamar release no objeto que, diferente da tal função free(), vai só decrementando o contador de referência. E quando quiser pode chamar o método release do pool, que por sua vez dá free de verdade nos objetos com contador zerado.</p>
<p>Nas versões mais novas do Objective-C e no Swift, coisa da última década, a Apple adicionou o ARC ou Automatic Reference Counting. Na prática ele vai cuidar de adicionar o código pra dar release do contador usando algumas dicas no seu código, durante a compilação. Vale a pena estudar em detalhes como o ARC funciona se você faz programas pra iPhone ou Mac, mas 90% dos casos você não se preocupa tanto com a memória e vai precisar saber mais quando tiver bugs ou quiser otimizar o uso de memória, por exemplo forçando a limpeza do pool mais vezes. Digamos que você está lendo um arquivo com 500 linhas, em vez de fazer o loop e só no final limpar tudo de uma vez, você pode ir dando release no pool manualmente a cada 50 linhas por exemplo e evitar que o sistema aloque mais memória do que precisa. Se você desaloca memória com muita frequência, vai usar mais CPU e diminuir a performance geral, por outro lado vai consumir menos memória. Se você desalocar com pouca frequência, aumenta a performance, mas também aumenta o uso de memória.</p>
<p>Um problema de contagem de referências é o caso de reference cycle ou seja, você tem objeto A com uma referência pro objeto B daí no B fazemos uma referência pro objeto A, agora ambos os contadores de A e de B nunca vão ser menores que 1, e o pool não vai conseguir desalocar a memória desses objetos mesmo que ninguém mais esteja usando, porque os contadores não são zero. Por isso em Objective-C e Swift você tem a diferença entre strong references que são as referências normais. Mas também temos referências weak e unowned, nenhuma das duas incrementa o contador e isso quebra o ciclo de referências. No caso a referência do objeto B pro objeto A poderia ser declarada como Weak e daí o contador de A chegaria a zero normalmente e ambos poderiam ser limpos. Novamente, você precisa ler em mais detalhes a diferentes de uma referência Weak e Unowned que vou deixar nas descrições abaixo. Linguagens com contadores de referência como Python costumam ter a opção de referências fracas ou Weak.</p>
<p>Controlar os contadores de uso de cada objeto é uma forma de você não precisar manualmente liberar objeto a objeto quando não precisa mais. Basta ter um controlador como o NSAutoreleasePool que consegue limpar todo objeto com contador zerado de uma vez só. Em C++ temos smart pointers que funcionam mais ou menos parecido. Um garbage collector muito simples poderia simplesmente ser uma thread que, de tempos em tempos, manda limpar esse pool. Seria uma forma rudimentar de garbage collector. Mesmo Python trabalha com contador de referências também, mas seu collector é mais sofisticado do que só isso.</p>
<p>Eu falei que em Objective-C você pode criar quantos pools quiser e controla eles manualmente. Outra forma é esse Pool ser tipo uma estrutura global e única, é como funciona em Ruby e em Java e acho que em .NET também. Pense numa lista gigante, onde sempre que precisarmos de memória pedimos pro alocador que vai adicionando nessa lista também, catalogando todos os elementos que vão sendo alocados. Toda vez que você passa a referência desse elemento pra outro objeto, ele vai criando uma árvore de dependência. Objeto A pode ter referências pros objetos B e C, e o objeto C tem referência pro objeto D. Depois de algum tempo, a variável que aponta pra A deixa de existir, por exemplo o método retornou ou você passou nulo pra variável e então ninguém mais aponta pro objeto A.</p>
<p>Com o tempo vários objetos vão ficando nesse estado, onde ninguém mais aponta pra eles a não ser a lista global que falei. Então de tempos em tempos um processo especial chamado collector, numa thread no interpretador ou na VM, poderia ir vasculhando essa lista por todos os objetos que ninguém mais está apontando e ir só marcando. Depois um segundo processo poderia ir na mesma lista, elemento a elemento, desalocando os objetos que foram marcados. Então se a lista tinha 1000 objetos, cada um ocupando 1 slot da memória, na fase de marcação 500 objetos foram marcados como não usados, e na fase de limpeza esses são liberados os 500 slots pra uso. Sendo ultra simplista é isso que chamamos de um garbage collector Mark and Sweep. É um collector de duas fases, onde em cada fase ele precisa vascular elemento a elemento da lista de alocação, fazendo um mark na primeira fase e sweep na segunda.</p>
<p>Porém, quando eu falo desalocar não quer dizer devolver memória pro sistema operacional. Lembra do problema de fragmentação? Se eu ficar devolvendo memória toda hora, muito rapidamente a memória do processo vai ficar fragmentado. Linguagens como Ruby ou Java começam pedindo um certa quantidade de blocos de memória pro sistema operacional. No caso de Ruby tem o equivalente de chunks que ele chama de slots, cada slot tem um tamanho fixo de 40 bytes onde cabe toda a estrutura que define um objeto de Ruby.</p>
<p>Então ele só lida com slots fixos de 40 bytes. Um conjunto desses slots é chamado de Heap. Não confundir com o Heap do malloc de C, é o mesmo nome mas tanto Java quanto Ruby chamam conjuntos de slots ou chunks de Heaps. No caso do Ruby existem várias variáveis de sistema pra configurar o alocador, como por exemplo o RUBY_GC_HEAP_INIT_SLOTS, que por padrão o valor é 1000. Um heap de Ruby costuma ter 16 kbytes. Ou seja, ele vai alocar de 40 bytes x 1000 que seria quase 40 kbytes ou pelo menos 2 heaps.</p>
<p>Se acabar os slots disponíveis, o alocador do Ruby vai pedindo pra alocar mais heaps, vários slots de uma só vez. Digamos que ao carregar seu programa ele precisou de 10 mil heaps, ou seja, uns 156 megabytes. Agora digamos que depois de rodar por um tempo, uns 500 mil slots de objetos foram marcados como não usados mais. Na fase de Sweep você imaginaria que ele vai devolver 500 mil x 40 bytes que dá quase 20 megabytes, de volta pro sistema e em vez de ocupar 156 megabytes ele vai passar a usar 136 megabytes. Mas não, no final do Sweep seu programa vai continuar usando os mesmos 156 megabytes. Por que? Porque ele só vai liberar os slots da lista global, mas a memória de verdade continua alocada. Quando você criar novos objetos nos slots livres, ele vai reusar e escrever em cima do mesmo trecho de memória de antes.</p>
<p>De cara temos estratégias diferentes entre o Heap de uma linguagem como Ruby  ou o Heap de uma linguagem como Java. No caso de Java, todos os objetos ocupam espaço no Heap. No caso de Ruby só a estrutura e dados muito pequenos ocupam os slots de 40 bytes. Mas um array ou um string costuma ser muito maior que 40 bytes, então o Ruby chama a syscall malloc do sistema operacional e guarda o endereço alocado no slots de 40 bytes apontando pro espaço fora do Heap. A vantagem do Ruby é que todo objeto tem o mesmo tamanho dentro do Heap e isso ajuda a não fragmentar tanto, mas ele depende do alocador do sistema operacional pros blobs de dados maiores. No caso do Java ele não tem que se preocupar com o sistema operacional, porque ele usa só sua memória interna do Heap, mas outro lado isso vai causar fragmentação interna e ele tem uma forma de evitar isso, que vamos ver já já. Mas só pra entender que diferentes collectors organizam seus dados internamente de formas diferentes.</p>
<p>Do ponto de vista de programação, se você ainda não entendeu, alocar memória desnecessariamente vai aumentar o tamanho dos heaps ou a quantidade de heaps, dependendo do alocador, e isso vai bloquear memória do sistema operacional e causar fragmentação desnecessária também. Você não pode achar que vai alocar 1 giga de dados, devolver esses 1 giga, e vai continuar tendo bonitinho 1 giga de novo sobrando. Você vai fazer o alocador ter que trabalhar muito pra evitar a fragmentação e se seu sistema tiver pouca memória geral, provavelmente não vai conseguir ter esse espaço livre de volta.</p>
<p>Exemplos de má programação é o famoso caso de abrir um arquivo gigante de vários gigas na memória, ou conectar num banco de dados e não fazer o select com os critérios de where certos e acabar puxando muito mais linhas da tabela do que precisava. Tudo isso vai ocupar espaço, o alocador vai ter que esticar os heaps, mais memória do sistema vai ser consumido, e não necessariamente ele vai liberar essa memória de volta pro sistema. Mesmo depois que o collector limpar os slots ocupados por esses dados, você ficou com um heap inchado e fragmentado. E se seu servidor for pequeno, você vai começar a ter problemas de memória mesmo que teoricamente tenha “memória sobrando” ainda.</p>
<p>Se seu programa rodando em produção tem leaks de memória, normalmente são dados que vem da rede, como dados do banco de dados, ou processamento de arquivos grandes. O ideal é sempre seu código tentar usar memória até um limite. E quando precisa puxar dados externos, ter algumas checagens pra ver se não vai usar memória desnecessariamente. Tipo checar o tamanho de um arquivo antes de carregar ele inteiro na memória. Se for arquivos pequenos de alguns kbytes, dá pra ler tudo e armazenar numa variável de string. Mas se for um arquivo de gigabytes, é melhor abrir como um Stream e ir lendo pedaços do arquivo de cada vez. Ou quando precisa puxar dados de uma tabela num banco de dados, usar coisas como um limit no select pra limitar quantas linhas vai puxar de uma só vez. Famoso caso que o amador fala que na máquina dele funciona, só que na máquina dele ele só testou com arquivos pequenos e com tabelas com 10 linhas de teste. Por isso quando vai pra produção tem esse comportamento. Isso porque é um programador que não tem noção de gerenciamento de memória.</p>
<p>Voltando, outro problema é se seu programa é grande e aloca milhões de objetos. Toda vez que o processo de Mark e Sweep rodar, eles precisam passar por todos eles, um por um, toda vez. Mesmo que só leve uma fração de milissegundo pra marcar e sweepar cada objeto, ainda assim vai levar dezenas de segundos pro processo todo se contar milhões de objetos alocados. É muita coisa. Pior ainda, toda vez que você vai marcar os objetos ou limpar, precisa pausar o programa todo, é o que chamamos de Stop the World. Isso porque você tem que garantir que o programa rodando ao mesmo tempo que o collector não se corrompa tentando acessar um objeto que acabou de ser desalocado. Só que se você pausar seu programa por muitos segundos, ele vai ficar muito lento.</p>
<p>Ou seja, carregar dados desnecessários não só desperdiça memória como desperdiça performance. Agora você só tem 2 escolhas. A primeira é rodar poucas vezes o Mark e Sweep, mas aí você arrisca consumir muita memória e fazer seu heap crescer demais já que sempre que precisa de mais memória e não tem slots pra reusar ele vai pedir mais pro sistema operacional. E eu já disse que toda memória que ele aloca do sistema ele não devolve mais ou vai demorar pra devolver pra evitar fragmentação, daí seu processo vai crescer mais do que devia. A segunda escolha, se você quer evitar isso então tem que rodar mais vezes o Mark e Sweep só que daí a performance geral do programa vai ficar lento. Ou seja, agora você tem que escolher entre performance ou uso de memória.</p>
<p>Pra resolver esse problema é que existe a idéia de Generational Garbage Collectors. Ele vai dividir a memória interna do interpretador ou da máquina virtual em 2 ou mais gerações. Java divide seus heaps em grupos chamados Young, Old e Permanent (por isso você vira e mexe vê configuração em tutorial de JVM pra configurar coisas como o Permsize que é o tamanho desse espaço permanente). O garbage collector G1GC novo do Java chama de Eden, Survivor e Old. Python chama só de geração zero, um e dois.</p>
<p>A idéia vem de uma hipótese chamada Weak Generational Hypothesis. Em linhas gerais essa hipótese diz que objetos novos tendem a morrer jovens e objetos velhos tendem a ficar ativos por muito tempo. Parece óbvio quando falamos assim, né? Eu já expliquei como o sistema operacional divide a memória em arenas por exemplo, pra evitar contenção de threads. Agora vamos dividir mais ainda pra evitar passagens desnecessárias por objetos que não vão morrer tão cedo.</p>
<p>O problema que um generational garbage collector quer resolver é diminuir ao máximo as pausas da fase de mark and sweep. A forma de fazer isso é evitar ficar olhando objetos que nunca vão ser limpos. Por exemplo, objetos singleton que seu programa carrega no começo e vai usar até morrer. Num Ruby, toda classe é um objeto. Mas as classes raramente precisam ser limpas, então pra que perder tempo olhando pra elas toda vez? Em resumo, na primeira vez que o mark passar, ele vai ter que olhar tudo. Objetos que sobrevivem a múltiplas passagens da fase de mark podem ser promovidos da geração Nova pra geração Velha e se sobreviverem mais algumas passadas de mark, podem ser promovidas pra geração permanente.</p>
<p>Podemos dividir as fases de Mark em Minor e Major. O Minor só olha pros objetos mais novos e ativos na geração Nova. O Major olha também pra geração Velha pra ver se algum precisa ser limpo. Mas uma ver promovido pra permanente, podemos esquecer desses objetos. Digamos que um programa normal tenha 100 mil objetos, e que depois de algumas passadas da fase de Mark 50 mil deles tenham sido promovidos pra geração permanente. Os outros 50 mil são objetos que morrem, e novos que são criados e na média ficam sempre uns 20 mil na geração nova, uns 30 mil na geração velha. A performance total da fase de mark é pelo menos o dobro, porque você no mínimo vai passar por metade dos objetos toda vez. E tende a ser mais rápido que isso porque você passa menos vezes na geração velha também. Então é um ganho enorme de performance.</p>
<p>Numa linguagem como Ruby ou Python, esses segmentos como eu disse são só listas com ponteiros pros slots ocupados. Como por baixo é tudo estrutura em C temos endereços e como as extensões em C podem precisar do endereço exato de determinada estrutura não podemos mover os dados de lugar.</p>
<p>Eu menciono isso porque em Java temos uma coisa extra. Java não lida diretamente com ponteiros, ele trabalha com referências no nível da linguagem. Uma variável em Java aponta pra uma referência e a referência, por baixo dos panos, é indexada num endereço. Mas o endereço pode mudar. Diferente de Ruby ou Python, em baixo nível o alocador pode mover o objeto de um slot pra outro nos heaps. Mover fisicamente vai mudar o endereço e aí eu mudo na referência. Mas no alto nível a variável no Java só vê a referência então pra ele nada mudou. Uma coisa é eu dar um produto na mão de uma pessoa. Agora não posso mais tirar o produto da mão dela sem ela reclamar. Outra coisa é eu dar um vale produto pra pessoa. Agora no estoque tanto faz se eu mudar o produto de lugar, o que vale é a referência.</p>
<p>Isso é importante porque na JVM quando acontece a fase de promover os objetos da geração jovem pra geração velha, em vez de só mover o ponteiro de um segmento pra outro de uma lista, na verdade eu estou movendo o objeto de um slot da geração jovem pra um slot da geração velha, fisicamente falando. Estou copiando de um slot pro outro e depois liberando o primeiro slot. Então fisicamente na memória, os heaps de Java tendem a ser menos fragmentados que num Python ou Ruby.</p>
<p>E os objetos de Java podem alocar tudo dentro do objeto em vez de usar a estratégia de dividir o objeto em 2 partes, sendo uma estrutura de tamanho fixo e o resto dos dados alocados fora dos heaps via malloc do sistema operacional. Nesse caso os objetos de Java tem tamanho variável, o que causaria fragmentação como eu já falei mas como ele pode mover os objetos de lugar e ir defragmentando a cada passada do collector, então evitamos fragmentação. É como se o collector do Java fosse também ao mesmo tempo um defragmentador e de fato ele tem uma fase chamada compaction, que move objetos de lugar pra defragmentar se o coletor perceber que o heap acumulou muita fragmentação.</p>
<p>Se não ficou claro, nos diagramas que mostrei até agora, parece que todos os objetos estão movendo de um espaço de memória pra outro. Isso é verdade no Java. Mas num Ruby os objetos não se movem de lugar, os endereços são fixos. Faz de conta que existam listas como uma lista ligada ou um array grande. Cada elemento da lista é um endereço pra memória real, e as listas são as gerações. Então quando eu falo que o objeto é promovido de uma geração pra outra, na memória real nada acontece, só o endereço que sai de uma lista e vai pra outra lista. Num Java, a estratégia de gerações a ajuda a diminuir fragmentação porque à medida que os objetos são promovidos eles movem de lugar na memória e isso vai defragmentando. Mas num Ruby a fragmentação se mantém a mesma, o que vai melhorar é a velocidade do Mark and Sweep.</p>
<p>Esse tipo de processo é o que chamamos de um Compact Copying Collector. A principal função é diminuir a fragmentação da memória. Se você tinha curiosidade, os garbage collectors do Erlang e do .NET são similares. Eles são generational, e tem copy collectors. Os princípios são os mesmos. Só linguagens que lidam com ponteiros e endereços físicos diretamente como as linguagens que compilam nativamente sem um runtime que abstrai a memória ou os interpretadores, não tem como fazer compact ou copy e tendem a fragmentar mais. Não quer dizer que é impossível fazer a parte de copy. Se você mudar um objeto de lugar, ele ganha um novo endereço, agora você precisa garantir que quem tente acessar o endereço antigo caia no novo, algo como uma barreira de leitura. Um dos meus problemas com acesso a ponteiros não é só por aritmética de ponteiro, mas porque dificulta defragmentação via compact copying. Era o que o Go pretendia fazer, mas se não me engano até agora eles não implementaram a fase de copy.</p>
<p>Além disso podemos executar partes dessas fases concorrentemente e paralelamente usando threads reais. Por isso em JVM você ouve falar de nomes como Parallel GC ou ConcurrentMarkSweep GC. O primeiro usa múltiplas threads. O segundo só uma thread, mas ele pausa o mundo só na fase inicial de mark e sweep, mas depois ele roda paralelamente à aplicação. JVM pra servidor usava o Parallel GC por exemplo. Hoje em dia temos o G1GC que usa gerações menores de segmentos 0, 1 e 2 ou Eden, Survivor e Old como expliquei acima, mas ele usa múltiplos segmentos de cada geração em vez de segmentos contíguos e outros algoritmos.</p>
<p>O problema de rodar concorrentemente é a fase de Mark and Sweep, que exige a tal pausa do programa ou Stop the World. Pra evitar isso, linguagens como Erlang implementam uma versão melhor que é mais custosa mas evita a pausa, chamada Tracing collector. Pense uma fase de mark dividida em 3 fases, como se fossem gerações, usando alguma variação do algoritmo chamado Tri-color, onde determinamos cores como preto, cinza e branco e no mark os objetos são movidos pra preto ou branco, e no final quando nenhum objeto for cinza, podemos fazer sweep de todos os objetos que sobraram com a cor branca. A vantagem disso é que podemos fazer essa marcação sem precisar pausar o programa. O Incremental Mark and Sweep do Ruby usa estratégia de Tri-color pro que se chama de Minor GCs ou fases de mark mais rápidas.</p>
<p>O collector do Java tem vários outras estratégias além do que expliquei até agora. De qualquer forma, o que chamamos de um garbage collector, completo, poderia ser um “Parallel Concurrent Generational Incremental Mark and Sweep Copy Compactor Collector”. Ruby e Python fazem algo similar, com a limitação de não poderem ser copy collectors nem rodar collectors concorrentemente. Javascript faz a mesma coisa também.</p>
<p>Generational garbage collectors tendem a usar mais memória pra manter todas as estruturas e segmentos pra cada geração. Ou seja, é um overhead mais alto do que o jeito do Objective-C e Swift. Linguagens compiladas nativamente com um runtime pequeno como C ou Objective-C tendem a depender principalmente do alocador do sistema operacional como o ptmalloc2 ou jemalloc. Linguagens interpretadas são híbridos, ele tem um espaço de heaps alocados com alocador em user-land pra gerenciar os objetos mas usam o alocador do sistema operacional pra blobs maiores como blocos de texto ou arrays grandes.</p>
<p>Uma JVM, se não me engano, pede pro sistema operacional alocar um espaço grande no começo do boot, mas depois ele se vira só em user-land. Vai usar mais memória, mas como não depende de syscalls, tende a ser mais rápido. Ao mesmo tempo, linguagens compiladas e interpretadas dependem do alocador do sistema operacional pra gerenciar a fragmentação de memória como já expliquei no episódio anterior. Uma JVM controla a fragmentação ele mesmo via o processo de copy collector onde ele consegue mover os objetos de lugar.</p>
<p>Falando no alocador do sistema operacional, o ptmalloc2 que é o malloc do glibc que a maioria das linguagens usa, aloca arenas pra cada thread real como já expliquei no episódio anterior. Pior ainda, ele não tem thread-cache ainda como o tcmalloc ou jemalloc. Portanto quanto mais arenas ele alocar, mais memória vai desperdiçar. E o multiplicador pra máquinas 32-bits são de 2 vezes o número de threads reais ou seja, máquinas quad-core com hyperthreading que dá 2 threads por core significa pelo menos 16 arenas. Mas em máquinas 64-bits o multiplicador é 8 então são 8 vezes 4 cores vezes 2 threads que dá 64 arenas!! É muita arena. Em estudos do Heroku que é uma plataforma de serviços para deployment de aplicações em ambiente virtualizado eles recomendam diminuir o teto de arenas. Em máquinas pequenas, com 2 cpus virtuais, em testes empíricos ficou notável que configurar o teto pra 2 arenas é mais que suficiente.</p>
<p>Segundo Hongli Lai, CTO da Phusion, o motivo desse multiplicador ser tão alto pode ser porque o principal desenvolvedor do alocador de memória é da Red Hat que vende produtos e serviços pro mundo enterprise. Lá a prioridade é aumentar performance mesmo que isso custe desperdiçar muita RAM. 10% a mais de performance por 30 vezes mais memória é uma conta que o mundo Enteprise topa pagar. Mas pra gente que precisa balancear performance com uso de memória pra escalar, a recomendação é simples, basta exportar a variável MALLOC_ARENA_MAX pra 2 e pronto. Só isso já vai evitar muitos vazamentos de memória por mau uso de arenas.</p>
<p>Gerenciamento de memória é um assunto absurdamente longo. Até este ponto eu expliquei tudo em linhas gerais. Cada parágrafo deste episódio tem uma literatura gigante que você vai precisar procurar pra entender em mais detalhes. Eu falei de copy collectors numa linha, como se fosse algo trivial, mas não é. Eu falei de thread-cache de malloc como se fosse uma correção simples do ptmalloc2 pro tcmalloc, mas não é. Eu falei como o jemalloc é mais rápido que o tcmalloc, mas procure os relatos de Jason Evans pra ver o tanto de trabalho que eles tiveram pra reescrever várias partes, várias vezes, até realmente chegar na versão atual. Alocadores de memória são super fáceis de escrever errado, de um jeito que vai ser super lento e consumir gigabytes de RAM à toa e ficar vazando em servidores. Nunca tente escrever seu próprio alocador a menos que tenha toneladas de dinheiro e tempo pra jogar fora.</p>
<p>Por outro lado, você precisa se interessar em estudar todas as formas que seu alocador funciona. Não basta ver quanto seu processo consome quando sobe. À medida que ele roda o garbage collector vai movendo objetos entre gerações, vai alocando memória do sistema operacional pras suas heaps internas que ele não vai devolver pro sistema operacional ou vai demorar pra devolver. Você precisa achar os pontos no seu programa que geram picos de consumo de memória, porque eles que vão pressionar o tamanho do heap e desperdiçar memória do sistema operacional.</p>
<p>Em última instância, você vai lembrar que todo collector tem dezenas configurações pra fazer tuning. Tamanho inicial do heap, fator de crescimento dos heaps, frequência pra rodar os collectors, quantidade de arenas, etc. Normalmente a configuração que já vem é o suficiente pra 99% das aplicações. Não seja ingênuo de achar que seu programa vai ficar muito melhor fazendo tuning dos alocadores ou garbage collectors, praticamente em todos os casos é seu código que desperdiça memória e tuning nenhum vai resolver isso. Mas no 1% dos casos que você já mediu exaustivamente o comportamento da aplicação em produção com dados reais, já corrigiu todo buraco de código possível, então talvez exista alguma chance de um tuning ajudar um pouco. Foi assim que na comunidade Ruby por exemplo descobrimos sobre o tuning de arenas do ptmalloc2 ou trocar pra jemalloc.</p>
<p>Em ambientes móveis, smartphones, é pior ainda, porque você não tem disco pra fazer swap. Significa que diferente de desktops e servidores, se seus programas foram mal feitos e alocarem memória de pico demais sem desalocar, o OOM ou Out of Memory Killer vai entrar em ação e matar seu programa com muita frequência, tornando a experiência do usuário uma droga. Por isso eu particularmente não gosto das soluções antigas de abrir aplicações que são só casca pra um navegador web e dentro carregar uma aplicação Javascript. No Javascript você não tem controle sobre o garbage collector e não tem acesso ao pool de memória como no num app nativo de iOS onde você pode mexer nos NSAutoreleasePool, significa que você está à mercê de um generational garbage collector, que usa mais memória que um app nativo, vai ter pausas de mark and sweep, e um Just in time compiler que também está alocando memória e usando mais CPU o tempo todo, porque otimizar código não sai de graça. Tudo tem um preço. Quanto mais alguma coisa é conveniente, menos ele tende a ser eficiente.</p>
<p>Quando você está programando pra desktop, que hoje em dia é tudo 64-bits, com muitos endereços virtuais sobrando, mesmo que falte RAM, seu sistema operacional vai jogar tudo pra arquivos de swap em disco. Fica lento, mas nada crasheia, por isso hoje em dia ficou comum fazer todo aplicativo desktop ser uma casca de navegador também e rodar apps web dentro. É o que Spotify, ou Slack ou os editores de texto Atom fazem. Consome memória pra caramba e quanto mais tempo eles ficam rodando mais memória vão consumindo. Por outro lado são super convenientes de fazer e você não precisa controlar memória manualmente. Eu ainda tenho preferência por programas nativos bem feitos que alocam memória de formas eficientes sem a necessidade de usar um garbage collector custoso. Um garbage collector é conveniente, mas não é necessário, especialmente em programas desktop.</p>
<p>Como eu já disse antes, tudo é uma questão de trade-off, você troca conveniência por mais lentidão e mais memória. Quanto menos conveniente for, mais você ganha performance e usa menos memória. Não existe um vencedor óbvio, cada caso é um caso. É um aplicativo pra servidor? Pra desktop? Pra smartphone? É grande? É pequeno? Precisa trabalhar quantidades grandes de dados com muita frequência? Precisa coordenar o processamento desses dados paralelamente? Ou de forma distribuída? Tudo isso afeta o que você vai escolher.</p>
<p>Com isso você também deve entender porque eu disse no episódio passado que linguagens com máquinas virtuais como Java ou Erlang preferem tomar conta de todos os recursos da máquina, porque o ideal é deixar eles alocarem toda a memória disponível pra gerenciarem os recursos internamente com seus garbage collectors. Percebam que eles levam pra user-land todas as atribuições do sistema operacional no kernel space. Eles fazem poucas syscalls porque conseguem gerenciar coisas como corotinas e gerenciar memória tudo em user-land.</p>
<p>O Go usa as funções de alocação como um malloc faz e prefere ficar mais baixo nível, mas ao mesmo tempo evitando depender demais do sistema operacional. Ele evita um pouco da fragmentação porque divide a memória em slabs dependendo do tamanho dos chunks como o ptmalloc e outros mallocs fazem. Mas diferente de Java e Erlang ele não tem e vai ter trabalho pra implementar um copy collector porque expõe ponteiros. É o mesmo motivo de porque Python ou Ruby também Não tem copy collectors. Generalizando um pouco, linguagens interpretadas ou com máquinas virtuais tendem a usar mais memória. Linguagens que não expõe ponteiros podem implementar compact copying collectors e evitar mais fragmentação ao custo de usar mais processamento.</p>
<p>E finalmente, chegamos ao último episódio do tema de Back-end! Gerenciamento de Memória era o último assunto pra vocês finalmente terem mais entendimento do funcionamento da máquina e sistema operacional. A discussão sobre linguagens só faz sentido se você entende essas coisas, senão a discussão fica mais parecendo fofoca de telenovela, sem argumentos e só repetindo frases de efeito que alguém disse num tweet ou blog post. Todo mundo só sabe até o ponto que a linguagem tem ou não garbage collector. E todo mundo só acredita que ter um generational garbage collector é bom. Mas ninguém sabe explicar porque é bom e o que você paga tem troca.</p>
<p>Mais do que isso, sem entender concorrência, paralelismo e gerenciamento de memória, você também não consegue entrar direito em devops e lá também vai ficar só seguindo superstições, mandingas, e repetindo o que ouviu os outros falar sem realmente saber o que está fazendo. Entender como a máquina e o sistema operacional realmente funcionam, pra mim, é o que começa a dividir as crianças dos adultos.</p>
<p>Se você tem dúvida mande nos comentários abaixo, se curtiu mande um joinha, compartilhe com seus amigos, não esqueça de assinar o canal e clicar no sininho pra não perder os próximos episódios. A gente se vê semana que vem, até mais.</p>
]]></content:encoded><category>performance</category><category>linguagens-de-programacao</category><category>ciencia-da-computacao</category><category>akitando</category></item><item><title>[Akitando] #44 - Concorrência e Paralelismo (Parte 2) | Entendendo Back-end para Iniciantes (Parte 4)</title><link>https://akitaonrails.github.io/2019/03/20/akitando-44-concorrencia-e-paralelismo-parte-2-entendendo-back-end-para-iniciantes-parte-4/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/03/20/akitando-44-concorrencia-e-paralelismo-parte-2-entendendo-back-end-para-iniciantes-parte-4/</guid><pubDate>Wed, 20 Mar 2019 17:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/gYJSWs-gp1g"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Finalmente! Chegamos ao FIM do assunto sobre Concorrência e Paralelismo! Desta vez vou finalizar o que faltou falar sobre processos, threads, como eles se coordenam, quanto isso custa pro sistema operacional. E então vamos vamos sobre Green-Threads e como as linguagens modernas lidam com elas.&lt;/p&gt;
&lt;p&gt;Então finalmente posso dar minha perspectiva sobre as linguagens interpretadas como Python, Ruby, mais sobre Erlang/Elixir, Go, e como Scala, e outras linguagens se comportam em termos de concorrência e como se comparam com coisas como Javascript.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/gYJSWs-gp1g"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Finalmente! Chegamos ao FIM do assunto sobre Concorrência e Paralelismo! Desta vez vou finalizar o que faltou falar sobre processos, threads, como eles se coordenam, quanto isso custa pro sistema operacional. E então vamos vamos sobre Green-Threads e como as linguagens modernas lidam com elas.</p>
<p>Então finalmente posso dar minha perspectiva sobre as linguagens interpretadas como Python, Ruby, mais sobre Erlang/Elixir, Go, e como Scala, e outras linguagens se comportam em termos de concorrência e como se comparam com coisas como Javascript.</p>
<p>Este é o episódio mais longo da série até agora, e se você conseguir chegar até o final deve ter uma visão bem mais completa quando for discutir sobre linguagens. Então tenha um pouco de paciência.</p>
<p>Se você ainda não assistiu os vídeos anteriores, agora é a hora porque este vídeo vai convergir todos os assuntos que venho explicando desde que comecei a falar sobre Back-end.</p>
<p>Links:</p>
<ul>
<li>Named Pipe or FIFO with example C program (<a href="https://www.geeksforgeeks.org/named-pipe-fifo-example-c-program/"target="_blank" rel="noopener">https://www.geeksforgeeks.org/named-pipe-fifo-example-c-program/</a>)</li>
<li>Understand promises before you start using async/await (<a href="https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8"target="_blank" rel="noopener">https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8</a>)</li>
<li>Futures, cancellation and coroutines (<a href="https://medium.com/@elizarov/futures-cancellation-and-coroutines-b5ce9c3ede3a"target="_blank" rel="noopener">https://medium.com/@elizarov/futures-cancellation-and-coroutines-b5ce9c3ede3a</a>)</li>
<li>Futures and Promises (<a href="http://dist-prog-book.com/chapter/2/futures.html"target="_blank" rel="noopener">http://dist-prog-book.com/chapter/2/futures.html</a>)</li>
<li>Go concurrency considered harmful (<a href="https://medium.com/@sargun/go-concurrency-considered-harmful-26499a422830"target="_blank" rel="noopener">https://medium.com/@sargun/go-concurrency-considered-harmful-26499a422830</a>)</li>
<li>Go: the Good, the Bad and the Ugly (<a href="https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/"target="_blank" rel="noopener">https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/</a>)</li>
</ul>
<p>=== Script</p>
<p>Olá pessoal, Fabio Akita</p>
<p>O episódio da semana passada foi bem pesado em conceitos. Estamos no nono episódio da série Começando aos 40, episódio quatro do tema de Back-end, e a segunda parte especificamente sobre Concorrência e Paralelismo. Pra variar, eu tentei encurtar esse episódio mas ele vai acabar virando um dos mais longos da série de novo. Se você ainda não assistiu meus episódios anteriores do tema de Back-end, eu recomendo que pause agora e vá assistir, porque tudo que eu falei antes vai convergir hoje.</p>
<p>Como eu já disse, é um tema bem cabeludo e vamos tentar desembaraçar os últimos conceitos que vocês vão precisar saber pra conseguir seguir com seus estudos sozinhos. A pergunta que todo mundo fica na cabeça é: qual das linguagens é a mais performática? Ou, qual vai me dar a escalabilidade do Whatsapp e ao mesmo tempo ser mais fácil de programar?</p>
<p>Vários números podem parecer impressionantes. Muita gente adora repetir os X milhões de conexões simultâneas que um servidor do Whatsapp consegue suportar e chegam à conclusão que não existe nada melhor do que Erlang. Esse número é bacana, mas não é impressionante. Parem de se impressionar com números sem contexto. E parem de ficar escolhendo tecnologia baseado em blog post ou palestra de engenheiro do Netflix ou Facebook. É um absurdo sem sentido pra pessoas que deveriam ter como profissão raciocinar com lógica.</p>
<p>Repitam comigo: você não é o Netflix. Seu produto que ainda nem saiu do papel, não tem nem um usuário, você não precisa nem pensar na escalabilidade massiva de um Netflix, que em horas de pico pode representar ⅓ do tráfego total da internet dos Estados Unidos. Quando alguém me pergunta: Akita, queria fazer tal coisa igual do Facebook, será que dá? Minha resposta é a mais óbvia: claro que dá, você está disposto a PAGAR o mesmo que o Facebook pagou? Se sim, então claro, dá pra fazer igual ou melhor. Guardem esse pensamento, eu ainda vou falar sobre isso em mais detalhes em outro episódio, mas por agora só entendam esta verdade: foda-se mil conexões simultâneas ou 10 milhões de conexões, sua preocupação como iniciante tá a milhares de quilômetros de distância disso. Atenha-se a entender as tecnologias sem virar fã de marcas e ter metas fora da realidade.</p>
<p>(&hellip;)</p>
<p>Já entendemos os mecanismos básicos de como conseguimos ter concorrência e paralelismo. Mas na minha opinião o real problema é a coordenação. Não adianta nada você ter silício suficiente pra executar milhares de threads em paralelo se você não consegue coordenar o trabalho dessas threads. Então se tiver uma única coisa que você precisa levar dessa série toda é que a coisa mais importante nesse tipo de programação é conseguir a maneira mais simples e “barata” de coordenar tarefas concorrentes.</p>
<p>Vamos voltar ao que interessa. Você é um iniciante, finalmente entendeu os conceitos mais básicos de processos e threads. Aprendeu rapidamente sobre forks, sobre copy on write que ajuda a economizar memória. Mas você precisa entender alguns conceitos adicionais sobre coordenação agora. A unidade mais simples numa CPU é uma instrução. Uma instrução em Assembly, que usa como argumentos certos valores que você preenche em determinados registradores. Daí a instrução executa e o resultado é escrito pela CPU em algum acumulador ou outro registrador. Ele incrementa o contador de programa e vai pra próxima instrução.</p>
<p>É assim que a CPU começa a transformar o mundo ao seu redor. Tudo que você programa serve para pegar alguma informação que o mundo exterior vai fornecer, por exemplo o que o usuário digita ao programa, e você vai usar essa e outras informações para gerar algum resultado. Programação é basicamente isso: transformação de dados. Uma instrução simples de soma, pega dois números que você armazena um em cada registrador, a instrução executa o cálculo e transforma esses dois números num único que representa a soma deles, e grava em outro registrador. Essa instrução é basicamente o que você chamaria de uma função. Toda função recebe parâmetros ou argumentos de entrada, processa alguma coisa e retorna algum valor de saída. E você pode usar o resultado de uma função como parâmetro ou argumento de outra função, e então vai  encadeando uma função atrás da outra.</p>
<p>Quando você manda o sistema operacional executar o binário do seu programa, digamos um programa como wget ou curl no Linux, que você pode pensar como se fossem navegadores Web muito simples que rodam na linha de comando, você normalmente passa uma parâmetro, no caso uma URL de algum arquivo que você quer baixar. Você já viu ou vai ver um monte de tutoriais que usam um desses dois comandos pra baixar alguma coisa. O OS vai fazer o que já expliquei antes: alocar memória, criar um processo isolado, carregar seu binário, e passar esse parâmetro pro processo. Esse processo vai executar, no caso conectar no servidor que você mandou e baixar o tal arquivo. E quando terminar, o OS vai descarregar e liberar os recursos e o resultado, se tudo deu certo, pode ser um arquivo gravado em algum lugar no seu sistema de arquivos. Nesse caso o programa funciona como uma “função” também, ele tem uma entrada que é a URL que você passou de parâmetro e tem um retorno que foi o arquivo gravado. Ele transformou uma URL em um arquivo.</p>
<p>Em sistemas UNIX ou Linux, os shells como BASH ou ZSH, onde você roda na linha de comando, permitem encadear esses programas como se fossem funções. Quando seu programa carrega e vira um processo o sistema cria três canais unidirecionais: um canal padrão de entrada ou Standard In ou STDIN, um canal padrão de saída ou Standard Out ou STDOUT e também um outro pra erros, o STDERR.
Na prática pense que todo processo pode iniciar conectando alguma coisa no STDIN e alguma outra coisa no STDOUT. Por exemplo, podemos usar o tal programa CURL pra baixar um arquivo da Web mas em vez de gravar em um arquivo local podemos redirecionar o conteúdo baixado pra esse STDOUT e conectar no STDIN de outro programa, por exemplo, um programa como o famoso grep que filtra texto pra fazer pesquisa. (curl -s <a href="http://somepage.com"target="_blank" rel="noopener">http://somepage.com</a> | grep whatever)</p>
<p>Quando ensinei sobre processos já expliquei que processos sobem em espaços isolados de memória e o código interno do processo não interfere no espaço de memória de outro processo. O que eu acabei de falar sobre encadear processos redirecionando o STDOUT de um ao STDIN do outro, usando o que chamamos de Pipes é a primeira forma mais simples de comunicação e, portanto, coordenação entre processos, mas não é o único e nem necessariamente o mais útil, porque você só liga o final de um programa ao começo de outro programa e esses canais só tem uma direção, ou seja, o primeiro programa é quem manda coisas pro segundo programa, mas não o oposto.</p>
<p>Sem entrar em muitos detalhes hoje, existem outras formas de um processo se comunicar com outro. Como já disse antes existem os sinais como SIGINT. E você pode codificar seu programa pra ignorar isso ou fazer alguma coisa quando recebe esses sinais ou eventos. Novamente é mais uma forma rudimentar de comunicação. Um terceira forma rudimentar é que dois processos abram um mesmo arquivo, daí um deles pode ir escrevendo uma linha nova de cada vez nesse arquivo e o outro processo pode ir lendo essas linhas novas.</p>
<p>Expandindo nessa idéia de um arquivo comum entre dois processos, o sistema operacional oferece outra opção, os FIFOs que se você estudou alguma coisa de estruturas de dados, funcionam como uma fila, First In, First Out, ou seja, o primeiro elemento que entra é o primeiro elemento que sai. Você pode abrir um FIFO e dar um nome e abrir o mesmo FIFO a partir de outro processo, é quase como se fosse um arquivo. Agora os processos podem se comunicar e a essa estrutura damos o nome de Named Pipe. E chamamos de pipe porque é como se fosse um cano mesmo, passando informação de um lado para o outro. Eles são limitados como os pipes simples ou arquivos. Um dos processos abre o named pipe pra ler e o outro pra escrever e por isso também a comunicação só flui numa única direção.</p>
<p>Em todos os exemplos, um dos processos serve de produtor de alguma informação e o outro processo serve de consumidor. E o mecanismo no meio, seja um arquivo ou um named pipe, serve meio como uma fila. Eu acho que o mecanismo mais simples de comunicação entre dois processos é uma fila e o sistema operacional já oferece esses mecanismos faz décadas. Porém, no nosso mundo de Web, internet e programas que funcionam como servidores - que seriam os produtores - e programas que funcionam como clientes - que seriam consumidores, esses mecanismos não funcionam, porque não se comunicam pela rede. Mas mesmo na mesma máquina, sem depender de rede, podemos simular a comunicação entre um servidor e múltiplos clientes usando named pipes ou arquivos. Porém, cada novo cliente conectando no servidor exige um novo named pipe ou arquivo. Por isso existem os sockets.</p>
<p>No caso da internet falamos sobre sockets de IP que trafegam pacotes no formato TCP. Uma das vantagens de usar sockets é que o programa servidor se liga a uma porta no sistema como já expliquei e aceita conexões de múltiplos clientes. Mas diferente de arquivos ou named pipes, o servidor só precisa se comunicar através de um único socket, a partir de onde vai aceitando as conexões de múltiplos clientes. Além de só precisar de um único socket pra abrir esse canal, a comunicação é bi-direcional, então o cliente também pode enviar dados pro servidor e vice-versa, pelo mesmo socket.</p>
<p>No caso de Unix e Linux além de um socket IP existe o que chamamos de Unix Sockets. Ele tem propriedades parecidas com sockets de IP mas só servem para processos rodando na mesma máquina já que não é um socket de rede. É a maneira preferida de fazer dois processos se comunicarem de forma eficiente porque você só precisa de um socket no processo servidor pra atender múltiplos outros processos clientes, que normalmente chamamos de workers, e ele abre um canal bi-direcional, diferente de named pipes. No episódio anterior eu expliquei como o servidor web NGINX tem um processo master, que serve como um servidor, e ele cria diversos processos workers e, claro, eles se comunicam entre si via esses Unix Sockets, canais versáteis bi-direcionais. É como outros servidores que geram forks e workers fazem para seus processos coordenarem entre si, como um Postgres ou MySQL.</p>
<p>No mundo web, onde hoje temos os tais micro-serviços, eles são basicamente processos, programas servidores, que se ligam a uma porta de rede e abrem sockets IP aceitando conexões TCP e em particular oferecendo comunicação via o protocolo HTTP. Mas na prática são processos que se coordenam via sockets. Basta vocês entenderem que sockets, em particular sockets IP, obrigam um processo a se ligar a uma porta; e são canais bi-direcionais de comunicação. Em UNIX como BSD ou clones como Linux, podem ser Unix sockets para comunicação de processos na mesma máquina ou sockets IP para comunicação de múltiplas máquinas numa rede TCP/IP. E no nosso mundo Web essa comunicação normalmente se dá através do protocolo HTTP mas não é uma obrigação, podemos ter outros protocolos mais eficientes como o Protobuf ou Protocol Buffers que é um protocolo em formato binário, que é mais eficiente que o HTTP que é um protocolo texto, inventado pelo Google.</p>
<p>Aliás, lembrem que protocolos binários sempre são mais eficientes que protocolos em texto pelo simples fato que texto ocupa muito mais espaço. A explicação é muito simples. Num computador, representamos tudo no formato binário, 1 ou 0. Você já deve ter aprendido isso então vou resumir muito rapidamente. 1 byte são 8-bits. Em 1 byte você pode representar números inteiros de 0 até 255. Por que 256? Porque 2 elevado a 8 é 256. Uma palavra double byte ou seja 2 elevado a 16 representa números até 65 mil 536.
Um número como 1,000, cabe perfeitamente em 2 bytes, se for representado binariamente. Mas, se você preferir mandar o número 1,000 como um texto, isso seria um texto de 4 caracteres. Em um sistema moderno que reconhece Unicode ou UTF-8, cada letra precisa de 2 bytes ou mais. Existe uma tabelona que mapeia cada caracter de cada alfabeto do mundo a um código. Em alfabetos ocidentais, 1 byte já resolve, mas kanjis de japonês precisa de pelo menos 2 bytes por exemplo. Mas no caso do texto 1,000 que é um, zero, zero, zero, são pelo menos 4 bytes. Em resumo, o número puro sozinho, em binário, cabe em 2 bytes, mas sua representação em texto precisa de 4 bytes, ou seja, o dobro do tamanho. Iniciantes tem dificuldade de entender isso mas comece decorando, o número inteiro 1000 é um elemento totalmente diferente do texto de 4 caracteres “1000”.</p>
<p>Isso fazia mais diferença numa época onde as redes eram extremamente lentas, e hoje você não pensa muito nisso porque qualquer um pode ligar nas porcarias de uma Net ou Vivo e pedir internet de 50 Megabits ou mais. Mas se você tem um sistema que trafega milhões de gigas por hora, todo byte faz diferença, por isso pra muita coisa o Google usa o Protobuf em vez de HTTP em seus sistemas. Pro seu dia a dia, não se preocupe tanto, HTTP tá de bom tamanho mesmo porque hoje sabemos comprimir texto com algoritmos como GZIP que podem derrubar o tamanho de um texto longo até menos de um décimo do seu tamanho. Mas isso é assunto pra outro dia.</p>
<p>Eu expliquei tudo isso porque a comunicação entre dois processos, usando um protocolo pesado como HTTP, sobre TCP/IP, passando pros sockets que seu programa se conecta, vai precisar fazer uma coisa chamada marshalling ou serialização. Ou seja, internamente no seu programa o dado, digamos o tal número 1000 puro, está numa variável que provavelmente é um inteiro de 2 bytes. Mas quando você vai enviar pra fora do seu processo, como a exigência é o protocolo HTTP, você vai serializar ou seja, transformar o inteiro binário pra um texto e cuspir pelo socket. Do outro lado um outro processo cliente vai receber esse texto e vai ter que converter de volta pra um inteiro, ou seja, desserializar o dado. Muitos dos bugs modernos em micro-serviços, ou seja, comunicação de processos através de HTTP, vem de erros nessa conversão e desconversão. Fique esperto com isso.</p>
<p>Mas eu estou me adiantando. Tudo isso foi pra explicar as diversas opções que existem para comunicação entre processos, você tem coisas muito simples como pipes, named pipes, arquivos, unix sockets e até sockets IP. Pra fazer um programinha besta, que só roda localmente, falar com outro programinha, que também roda só localmente na mesma máquina, obviamente usar um protocolo como HTTP é dar um tiro de canhão pra matar uma mosca. É descomunal e ineficiente. Na pior das hipóteses você vai querer usar Unix Sockets. Se você programa em C existem bibliotecas como OpenMPI e OpenMP para facilitar programar comunicação inter processos na mesma máquina ou mesmo entre diversas máquinas.</p>
<p>Entendido como processos se comunicam, vamos entrar dentro de um processo e falar de Threads. Eu já expliquei nos episódios anteriores que uma Thread tem acesso à toda a memória interna do processo. Então se uma thread precisa se comunicar com outra thread, elas podem basicamente compartilhar uma mesma estrutura de dados. No caso de C digamos um Array. Ou uma lista ligada. Como fica tudo no mesmo espaço de memória, não existe a necessidade de você usar mecanismos fora do processo como um named pipe ou unix sockets, porque você acessa o dado diretamente no endereço da memória virtual do processo. Mecanismos externos que eu falei antes existem porque um processo não enxerga nenhum endereço de memória externo ao seu espaço virtual alocado pelo sistema operacional. Por isso comunicação entre threads é ordens de grandeza mais eficiente, mas com essa eficiência vem todos os problemas de mutex, semáforos, bugs de race condition e dead-locks que eu já expliquei.</p>
<p>Programar com threads é um processo que tem que ser extremamente cuidadoso da parte do programador. O sistema operacional não tem como te proteger e você precisa garantir que uma thread não vai escrever em cima do mesmo endereço onde já tem outra thread escrevendo ao mesmo tempo. Num sistema que só opera com uma thread real, esse problema meio que não existe porque o scheduler do sistema operacional vai deixar só uma thread realmente rodar e a outra precisa esperar a vez, o tal do time-slice que eu já expliquei. Mas em máquinas com 2 cores ou mais, realmente 2 threads podem estar rodando exatamente no mesmo instante, com paralelismo real, e é aí que os bugs que eu falei podem acontecer.</p>
<p>Como eu disse antes, o problema todo é coordenação. Pra piorar, vocês vão notar que existe a CPU com seus cores reais, o hardware de silício, que rodam threads reais, uma por core. E existem as threads que o sistema operacional organiza e gerencia. O tal scheduler do sistema operacional como o CFS do Linux, tem acesso a esses cores físicos, mas os programas podem mandar criar 100 Linux threads. Então numa máquina Intel i5 da vida, com quad-core e hyper-threading, existem 8 threads reais que podem rodar em paralelo em cada determinado instante, mas o sistema operacional pode gerenciar centenas ao mesmo tempo, fazendo seu scheduler ir dando um pouco de tempo pra cada uma, fazendo o tal context-switching que eu expliquei, tirando as coisas da mesa de uma thread, recolocando as coisas na mesa pra outra thread e dando vez pra ela.</p>
<p>Para gerenciar essas threads, fazer o tal context switching, o sistema operacional precisa guardar o tal contexto em memória pra cada thread. E eu já disse que uma thread vai gastar pelo menos 1 MB de memória. Então quanto mais threads você criar mais memória vai gastar. Pior, quanto mais threads você criar, maior o trabalho de context switching. E pior ainda, quanto mais threads você criar maiores as chances de seu programa ter aqueles bugs por não ser thread-safe. Existe ainda outro agravante, criar threads é uma atribuição do sistema, mais especificamente da kernel. Para seu programa pedir pra kernel criar threads você precisa fazer system calls, as syscalls que eu falei, e isso também não é barato.</p>
<p>Aliás, se você não sabia, vale definir. Você pode pensar que a kernel do Linux, drivers, os programas que vem numa distribuição, e os próprios programas que você escreveu, é tudo a mesma coisa. São todos binários que rodam na máquina. Se você fez o que eu recomendei e estudou um pouco de Linux, você sabe que existe o comando <code>sudo</code>, o comando que escala seu programa pra rodar com permissões de root ou administrador da máquina. Nesse caso você sabe no mínimo que existem diferenças entre rodar como um usuário restrito normal ou rodar como um root que tem acesso a tudo.</p>
<p>Mas na verdade o root não é a coisa mais poderosa do sistema. O Kernel é que é. No final das contas é o kernel que decide o que o root pode ou não pode fazer. Então se você só chegou até o root, vamos entender outra coisa. A única coisa que tem acesso irrestrito a tudo da máquina, todos os dispositivos, toda a memória e pode executar qualquer coisa, é a kernel. Na arquitetura de processadores Intel existem 4 anéis de permissão. A Kernel boota e roda no chamado Ring 0, ou anel 0, dentro desse anel qualquer código tem acesso a tudo. Todos os seus programas, inclusive os que rodam via sudo, rodam no Anel 3, o mais restrito de todos. É onde rodam os seus programas. Nos Anéis 1 e 2 rodam os drivers e coisas de virtualização, por exemplo o Virtual Box carrega a kernel do OS que ele vai emular no Anel 1. No Anel 3 várias instruções de Assembly da máquina não funcionam, como a instrução que muda de anel, obviamente, ou a instrução HLT que dá Halt e pára a máquina. O sudo simplesmente libera mais system calls pra kernel, mas o programa ainda vai ter que fazer syscalls e pedir coisas pra kernel.</p>
<p>Num processador ARM também existem Rings mas a numeração é ao contrário, o que ele chama de EL0 é o equivalente ao user land, ou Ring 3 da Intel. O EL1 é onde roda a kernel, eu não tenho certeza mas acho que seria mais equivalente ao Ring 1 da Intel, porque no EL3 roda o Hypervisor que vai ser tema talvez do próximo episódio. Daí você pode rodar múltiplos kernels no EL1 isolados um do outro.</p>
<p>O mais importante é entender que existe User land que é onde rodam todos os seus programas e Kernel space, onde roda a Kernel, a única entidade que tem real acesso a tudo do sistema. Chamar uma função dentro do seu próprio processo é extremamente barato. Chamar uma syscall é mais ou menos como chamar uma função em outro processo, então você pode imaginar que vai acontecer algo parecido com um context switch toda vez. Fora isso, existe um salto em privilégios do Ring 3 pro Ring 0. Tudo isso custa. Ok, são micro-segundos a mais toda vez. Mas pense um servidor web com threads, com milhares de usuários pedindo requisições HTTP e seu servidor criando threads pra cada uma e matando todas que vão acabando. Milhares por segundo, milhares de syscalls por segundo, agora as coisas começam a ficar caras.</p>
<p>Não tem nada mais rápido do que código que roda dentro do seu processo. Por isso você quer evitar fazer syscalls o máximo que puder. Criar milhares de processos ou milhares de threads custa memória, custa context switch, custa escalar privilégios entre Rings. Parece que o povo do Node.js estava correto em usar I/O assíncrono, certo? Calma, I/O assíncrono e event loops ou reactors é só “Uma” forma rudimentar de concorrência em sistemas que não suportam outros mecanismos como threads. E justamente, Javascript não suporta threads. Ele é mono-thread, então a sua única opção é usar o intervalo entre atividades de I/O como arquivos ou sockets pra tentar fazer alguma coisa. Mas isso é muito pouco.</p>
<p>Eu já expliquei uma das soluções no episódio anterior. É fazer o que o NGINX faz: múltiplos processos, na média um pra cada core da sua máquina, ou seja, um processo pra cada thread e cada uma delas com um reactor pra aproveitar que chamadas de I/O costumam gastar tempo pra completar. A grande vantagem num sistema como Node.js que é um processo de um único-thread é que você tem alguma concorrência graças às syscalls de I/O assíncrono do sistema operacional, e pelo menos não tem que lidar com as dores de cabeça de mutex e bugs de sincronia como race conditions ou deadlocks. Imagine o inferno extra que ia ser se Javascript além de tudo ainda tivesse que lidar com mais esse tipo de categoria de bugs.</p>
<p>Interpretadores que dependem bastante do sistema operacional por baixo, como Python ou Ruby possuem Threads reais. Porém existe um grande problema. Para ter boa performance, eles delegam muita coisa pra módulos escritos em C. O Scipy, uma das bibliotecas científicas mais famosas do mundo Python é quase inteira escrita em Fortran e C++, por isso ela é veloz, só uma casca é escrita em Python. Essas extensões ganham acesso às estruturas de dados internas dos interpretadores, é como se fossem parte do interpretador.</p>
<p>Agora vem o grande problema: elas não são necessariamente thread-safe, na verdade a maioria não é, e você não tem como saber.  Por causa disso, as Threads de Python e Ruby, apesar de mapearem para threads reais do sistema operacional e terem a capacidade de rodar em paralelo, acabam bloqueadas por um lock gigante do interpretador, que justamente ganha o nome de Global Interpreter Lock, ou GIL.</p>
<p>Com exceção de chamadas de I/O que conseguem rodar realmente em paralelo, as threads do interpretador são bloqueadas por causa do tal GIL. Elas ainda são úteis, mas não tanto quanto numa linguagem que não tenha o GIL. Na prática, assim como Javascript, tanto Python quanto Ruby acabam sendo essencialmente single-threaded, com algumas exceções como operações de I/O. Clones deles como Pypi ou JRuby, como não precisam manter compatibilidade com extensões em C, podem desligar o GIL e usufruir de threads reais.</p>
<p>Assim como Node.js em 2009, eu já expliquei como o Twisted em 2002 implementou a mesma solução de colocar um event loop numa thread pra atender milhares de requisições de I/O assíncronas e usar o intervalo enquanto espera essas chamadas completarem pra executar alguma computação e assim ter um jeito rudimentar de concorrência. Obviamente isso tem duas desvantagens, a primeira é que basta você fazer uma computação que demore demais e o event loop inteiro é bloqueado mesmo que a chamada de I/O já tenha terminado, já que roda tudo na mesma thread.</p>
<p>E a segunda desvantagem é que sua aplicação precisa ter muitas requisições de I/O pra compensar, ou seja, é melhor se usado em servidores de rede, como um servidor de aplicações Web. Esse modelo não funciona pra programas que são pesados em computação só. Por isso Node.js ou Twisted são indicados pra aplicações web. No mundo Ruby temos opções similares como Eventmachine ou o mais recente Async. No mundo Python, depois do Twisted também foi criado o Tornado e o Gevent que tem APIs mais modernas. Na prática é a mesma solução em todos eles: um event loop rudimentar que chamamos de reactor, que é gerenciado numa única thread, intercalando syscalls assíncronas de I/O e executando algum código nos intervalos.</p>
<p>Do ponto de vista da programação do código temos um problema. A forma como se programa um event loop é fazendo uma chamada de função assíncrona e passando como parâmetro uma segunda função que deve ser chamada quando o I/O acabar. Por exemplo, uma função de ler um arquivo de template de HTML na máquina recebe de parâmetro uma segunda função pra preencher esse template e gerar o HTML final. Num sistema single-thread, com tudo bloqueante, você escreveria essas chamadas de função uma embaixo da outra. Mas num sistema orientado a eventos, você precisa passar as funções subsequentes uma como parâmetro da outra. E isso cria o horroroso padrão de cascata ou o que eu gosto de chamar, de hadouken de funções. Se você ver algo assim hoje em dia, é um jeito antigo de escrever, é hediondo e não há ser vivo no planeta que veja algo nesse formato e não sinta imediatamente vontade de vomitar um pouco na boca.</p>
<p>No C já tínhamos algo parecido mas não necessariamente só pra concorrência, é o conceito de passar o ponteiro ou referência de uma função como parâmetro de outra função, chamamos isso de callbacks. Interfaces gráficas em VB ou Delphi ou Java Swing também tinham esse conceito com Listeners por exemplo. É um jeito rudimentar de se encadear funções onde uma depende do resultado da outra dentro de um loop de eventos. Toda interface gráfica é basicamente um loop também. Na prática, você tem um callback pra quando tudo dá certo e outro callback pra quando dá algum erro.</p>
<p>O Twisted do Python introduziu um conceito de código, os Deferreds. Em vez de chamar uma função, você cria um objeto que pode ser encadeado. Esse objeto basicamente engloba a tal chamada assíncrona, por exemplo, e você pode configurar esse objeto chamando métodos como addCallback. É quase a mesma coisa que antes, mas como estamos encapsulando a função num objeto, ele fica um pouco mais maleável de se trabalhar no seu código sem precisar virar um callback hell ou hadouken que falei antes. No mundo Web o framework JQuery adicionou a mesma construção com o mesmo nome, Deferreds. Assim você podia configurar uma chamada Ajax e conectar callbacks.</p>
<p>Na prática você encapsula uma chamada assíncrona num objeto, que você vai “deferir” a execução pra algum ponto no futuro, normalmente depois de ter a oportunidade de configurar esse objeto com callbacks ou esperar outros deferreds ficarem prontos e executar tudo de uma vez só, por exemplo. De qualquer forma é uma forma melhor do que ter hadouken de callbacks.</p>
<p>Retomando, fazer syscalls é caro. Context Switching é caro. Chamar coisas, funções, dentro do próprio processo é rápido e barato. Dentro do seu processo existe seu código transformado em binário. E você tem suas funções lá. Funções que demoram demais pra executar bloqueiam todo o seu processo ou thread. E se fosse possível pausar uma função antes dela acabar, deixar ela de lado, e dar chance de outra função rodar dentro do mesmo processo? Não estou falando de criar 2 threads, estou falando de na mesma thread a função ser pausada, deixar outra executar e daí voltar a executar a primeira. É como se fosse uma thread mas como é tudo dentro do mesmo processo, sem syscalls, sem context switching. É pra isso que serve uma corrotina, pense uma função com múltiplos pontos de suspensão, que você pode continuar ou resumir depois.</p>
<p>Em particular, um caso especial de corrotina se chama Fiber ou Generator. Se você já brincou com classes de Enumerators e Iterators em Python ou Ruby, sabe pra que elas servem. Elas são basicamente funções ou métodos como qualquer outro. Mas de dentro desse método faça de conta que existe um loop gigante, indo até infinito. Significa que esse método vai bloquear tudo e você não vai rodar mais nada pra sempre. Mas de dentro desse loop você pode chamar uma função, normalmente nomeada como yield, que vai devolver o controle pro código anterior que chamou o método. Agora esse código anterior pode executar outra coisa e quando quiser pode chamar o método resume, pra resumir a execução desse método com loop gigante, e ele vai dar yield de novo e assim por diante. Se você lembra de quando eu falei do Windows 3.1, isso seria algo parecido com uma multitarefa cooperativa. Fibers são funções, ou objetos de execução, onde você codifica pontos de suspensão ou pausa da execução. A maioria das linguagens atuais têm algo assim, os nomes vão variar mas se você ouvir corotina, fiber ou generator é basicamente a mesma coisa. Python tem, Ruby tem, Javascript tem, Swift tem, Kotlin tem, todo mundo tem algo assim.</p>
<p>De qualquer forma, pra que servem essas corotinas que pausam e podem ser resumidas se elas não rodam em paralelo? Em princípio elas se parecem com threads: elas estão dentro do processo, portanto tem acesso a todas as estruturas de dados do processo. Porém, como elas não rodam em paralelo, não existe a situação de uma querer escrever em cima dos dados da outra. Você que tem que manualmente pausar e manualmente resumir uma fiber, portanto você tem controle. Por isso elas são mais simples de se trabalhar do que threads. Mas elas não rodam em paralelo, então se parecem mais com as threads de Python ou Ruby, que apesar de terem a capacidade de rodar em paralelo já que mapeia pra threads reais do sistema operacional, existe o GIL que vai bloqueá-las. Mas diferente de threads, não precisamos de chamadas pra Kernel, elas são simples abstrações dentro do seu programa, em user-land.</p>
<p>A existência dessa construção na linguagem facilita a programação de concorrência. ?Eu não disse que a forma rudimentar é uma função recebendo callbacks pra chamar outras funções quando vier o evento que determinado I/O finalizou? Depois inventamos Deferreds que já ajuda. Porém, podemos usar Fibers pra escrever o mesmo código que seria um hadouken de callbacks quase como se fosse programação imperativa tradicional, só que o yield bloqueia até a chamada voltar e daí ele resume daquele ponto. Então veja no exemplo como é um código hadouken
Agora veja o mesmo código usando fibers e a chamada de yield, e você vai ver como de repente do ponto de vista do código tudo ficou mais simples e sequencial. Então só por isso já temos um ganho absurdo em legibilidade e, por consequência, mantenabilidade do código.</p>
<p>Do ponto de vista do código, se somarmos um event loop com I/O assíncrono, conseguimos ter algum nível de concorrência e com fibers conseguimos ter um código que não é tosco. Estamos chegando em algum lugar aqui. Com isso Javascript com Node.js, Python com Twisted, Tornado ou GEvent, Ruby com Eventmachine ou Async, todos tem a possibilidade de usar essa arquitetura de reactors, porque no fundo quem realmente faz a mágica é a Kernel e suas syscalls assíncronas como epoll, kqueue ou IOCP. Porém, todas as bibliotecas que fazem chamadas síncronas, ou seja, do jeito bloqueante, precisam ser reescritas para usar esse recurso de fibers ou generators. Daí você entende porque em Python ou Ruby esse suporte é mais complicado e porque no Node.js é mais fácil: porque Node.js já nasceu com suas bibliotecas sendo criadas pra serem assíncronas.</p>
<p>Isso ajuda em linguagens que tem dificuldades com paralelismo de threads reais. Vamos falar das que tem acesso a threads reais sem o problema de um GIL ou lock global, como Java, ou C#. Pra elas existem Thread Pools. Em vez de ficar criando quinhentas threads a torto e a direito, sem controle, você limita quantas threads vão de fato existir simultaneamente. Como já disse antes uma média de uma thread por core da máquina. Tanto em Java quanto C#. Elas costumam ter alguma coisas como uma classe ThreadPool, literalmente um tanque de threads, e melhor ainda, quando tem outra abstração chamada Task ou Tarefa. A grande vantagem você vê num loop em uma lista gigante, onde você quer processar cada elemento dessa lista em paralelo. Só que se você ficar criando uma thread pra cada elemento, e a lista tiver quinhentos elementos, você vai acabar tento uns quinhentos threads pro sistema gerenciar. Ao passo que se você usar Tasks, que é só uma abstração, e configurar a ThreadPool pra ter só 5 threads, mesmo a lista tendo 500 elementos, você nunca vai ter mais que 5 threads rodando ao mesmo tempo.</p>
<p>Uma Thread Pool funciona como se fosse um load balancer de threads, vamos dizer. Nós usamos o conceito de Pool em tudo que é recurso caro. Por exemplo, bancos de dados nós usamos connection pools. Mesmo se tivermos 500 navegadores simultaneamente conectando no servidor de aplicação, não vamos criar 500 conexões no banco, vamos configurar um pool de conexões pra, digamos 100 conexões, e é só isso que vai existir, os primeiros 100 vão usar essas conexões, quando o primeiro acabar devolve a conexão pro pool e agora o próximo esperando vai pegar e usar e assim sucessivamente. A mesma coisa em ThreadPools. E esse é um dos principais conceitos que você tem que saber em arquitetura de concorrência e paralelismo: pools e filas. É como numa agência de banco. É dia de pagamento, 100 pessoas aparecem na hora do almoço, mas só vai ter 10 caixas. As 10 caixas representam o pool. E assim vamos tendo 10 pessoas sendo atendidas simultaneamente e as outras esperam na fila. É assim que o scheduler de threads funciona também. E é assim que podemos ter algum controle sobre as threads do sistema operacional.</p>
<p>No mundo .NET você tem classes como Parallel e Task que abstraem esses conceitos. Em Ruby temos também uma blblioteca chamada Parallel que vai abstrair pools de forks ou pools de threads. No mundo de Mac e BSD existe o Grand Central Dispatch e seus DispatchQueue. Enfim, cada linguagem ou sistema operacional vai oferecer alguma coisa que tem essa arquitetura. Alguma combinação de queues ou filas e pools. Preste atenção: você raramente quer usar Threads reais diretamente, especialmente em grandes quantidades. Se precisar de threads sempre use Pools, Queues e Tasks.</p>
<p>Esse conceito também se estende pra computação distribuída. Até agora sempre falamos de um código que, na hora que precisa, carrega alguma forma de executar uma tarefa em paralelo. Ou um fork pra gerar um novo processo. Ou criando uma nova Thread. Ou agora criando Pools. Mas existe a opção de já ter essa segunda entidade pré-carregada e esperando. Um segundo processo, ou uma thread ou algo assim.</p>
<p>Em Java sempre tivemos no Enterprise Edition os JMS ou Java Message Service. No mundo Ruby on Rails usamos coisas como Sidekick, a aplicação conecta a uma fila representada num banco de dados Redis e existe um ou mais processos que ficam pré-carregados e ouvindo nessa mesma fila. Chamamos esses processos secundários de Workers. Temos isso em quase toda linguagem. Uma fila e vários workers, e como é um número limitado de workers é como se fosse um Pool também.</p>
<p>No mundo .NET temos opções como o projeto Hangfire. No mundo Python temos projetos como Celery ou RQ. No mundo Node.js temos coisas como o Kue. No mundo Java temos Message Queues no JEE ou Background Jobs no Spring. O conceito é sempre o mesmo: algum tipo de fila como RabbitMQ, Redis ou qualquer coisa assim, e diversos workers consumindo dessas filas. É uma solução de paralelismo que funciona tanto na mesma máquina quanto em diferentes máquinas, representando um tipo de computação distribuída. Outro nome que você vai ouvir associado a esses mesmos conceitos é Background Jobs.</p>
<p>Esse tipo de solução resolve a grande maioria dos problemas reais que temos hoje. Mesmo numa aplicação mono-thread, que tenha um reactor, ainda temos operações que custam tempo e processamento, que eu já expliquei que vão bloquear o event loop. Em vez de executar isso no processo principal, podemos enviar uma mensagem a uma fila e deixar outro processo, ou worker, fazer esse trabalho pesado, e assim deixar o reactor principal mais leve e mais rápido.</p>
<p>Mas ainda assim gostaríamos de alguma coisa mais eficiente dentro de um mesmo processo. E até agora não chegamos lá ainda. Mas se você prestou atenção aos episódios anteriores, existe sim uma construção pra definir concorrência em sistemas operacionais e hardware que não tem mais do que uma thread real. Uma green thread. Ou seja, uma thread que só existe em User land e não em kernel space. É o que o protótipo do Java original tinha no começo quando foi desenhado pra set-top-boxes, tipo Tivo como eu expliquei em outro episódio. Um conceito de hardware barato do começo dos anos 90 que não rodaria nada muito mais potente que um DOS. Se quiséssemos programar concorrência, não haveria threads reais, mas mesmo assim podemos programar concorrência com green threads, só que elas nunca vão rodar em paralelo. Lembram? Uma coisa é ter concorrência, outra coisa é ter paralelismo. Ter paralismo implica ter concorrência, mas não o oposto.</p>
<p>Corotinas, fibers ou generators que expliquei acima, seriam mecanismos para chegar em green-threads: funções com pontos de suspensão, onde podemos gerenciar várias delas ao mesmo tempo, e ir executando um pouco de cada uma de cada vez. À primeira vista, hoje que temos máquinas multi-core com múltiplas threads paralelas reais, pra que serviriam green-threads? Pra muita coisa. Eu já repeti várias vezes e vou repetir de novo: threads custam memória, talvez 1 MB, tem context switching caro, executado pelo scheduler da kernel, tem syscalls que precisam pular de user-land pra kernel space. Green threads são baratas, dependendo da linguagem pode custar talvez na ordem de 2 quilobytes, ou seja umas 500 vezes mais leve que uma thread. Elas estão dentro do mesmo processo, em user land, e não dependem de syscalls nem de context switching da kernel. Ou seja, comparado com threads, elas são realmente muito mais baratas. Mas tem um problema: só com o que sabemos até agora, elas não rodam em paralelo.</p>
<p>Aí que você se engana. E se, usássemos o conceito de thread pools e fizéssemos um código que pegasse milhares de green-threads e mandássemos executar num pool de threads reais? Basta construirmos um scheduler em user-land que se encarregaria de fazer esse load balancing de green-threads entre threads num pool. E muito bem chegamos à base de um Scala, Clojure, Go ou Erlang e, por consequencia, Elixir.</p>
<p>Erlang, nos anos 80, fez o seguinte: quando sua VM Beam sobe, ele sequestra os recursos da máquina pra ele: o I/O, a memória e principalmente as threads reais do sistema operacional. Ele roda um scheduler em user land pra cada thread real. As funções em Erlang não são só fibers, elas são corotinas, e a VM tem o poder de arbitrariamente pausar uma função quando bem entender, ou seja, ela não depende que você tenha programado um yield na função pra devolver o controle, ela vai pausar quer você queira ou não, exatamente como a Kernel do Linux faz com seus programas. Ou seja, o Erlang implementa o equivalente a um scheduler preemptivo de green threads em user land, e em vez de fibers ela tem corotinas de verdade.</p>
<p>Mas o Erlang tem algo mais importante ainda. Um dos problemas de uma thread é que se ela tem problemas, exceções, erros e outros bugs, ela pode corromper toda a memória do processo. Então basta uma thread fazer bobagem e seu processo inteiro pode crashear. Pra evitar isso o Erlang resolveu que cada uma dessas suas corotinas não compartilhasse nenhuma memória com outras corotinas. Na verdade, pra dificultar nosso vocabulário, dentro do Erlang ele chama essas corotinas que estão executando de “processos”, não confunda com processos de verdade de um Linux, apesar do comportamento ser similar. Existem funções normais como de qualquer outra linguagem, mas existem esses processos de erlang. Como esses processos não enxergam memória de outros processos, se você quiser fazer um processo se comunicar com outro temos o equivalente a Unix Sockets se fossem processos de Linux, canais bi-direcionais por onde eu posso enviar mensagens.</p>
<p>Essas mensagens são estruturas de dados, de novo, não compartilhadas. A VM do Erlang, assim como um OS Linux, aloca espaços de memória protegidos pra cada processo. Mas diferente do kernel Linux, que aloca 1 MB pras estruturas de um processo ou thread, o Erlang não precisa alocar mais que 2 quilobytes pra cada processo. O processo vai recebendo mensagens num mailbox, um tipo de fila e escolhe o que fazer com essas mensagens quando quiser. Quando um processo termina ou crasheia, existe um garbage collector pra cada processo individualmente. E isso pra mim é o que diferencia Erlang de todo o resto, um processo, que é um green-thread, com bug não afeta e nunca corrompe o resto do sistema já que ele não compartilha nada.</p>
<p>Eles não compartilham ponteiros nem nada e por isso nunca tem a capacidade de desestabilizar o sistema. Por isso eu costumo dizer que a VM Beam do Erlang é quase como se fosse um mini-Linux. Pra ficar mais similar ainda, os Linux tem um processo master que dependendo da distro pode ser um initd ou systemd ou outro que sobe daemons, que já expliquei antes, são processos especiais, serviços do sistema. No Erlang temos a mesma coisas só que se chamam Supervisors. Eles se encarregam de quando você subir sua aplicação, no boot da VM do Erlang, vai subir os supervisors que vão iniciar os processos necessários e garantir que se um deles crashear ele vai subir um novo processo no lugar.</p>
<p>Eu expliquei em 2 minutos o Erlang, mas pense em Erlang mais como um Java: uma máquina virtual que te oferece diversos serviços, incluindo coisas que o Java ou .NET não tem: schedulers e processos. Esses processos são green-threads. Tudo roda em user-land, e por isso é extremamente barato. É tão barato de fato que podemos fazer o que eu expliquei que fazíamos antigamente: toda nova conexão de rede antes a gente gerava uma thread real pra lidar com ela. Mas com o C10K descobrimos como é caro e não escala ficar criando threads reais o tempo todo pra toda conexão. 10 mil threads consumiria 10 gigabytes de RAM. Mas 10 mil processos Erlang consome só 19 Megabytes. Portanto, como essa green-thread é ordens de grandeza mais barato, podemos voltar a criar uma green-thread por conexão se quisermos. Entenderam a diferença? Green-threads são ordens de grandeza mais eficientes que uma thread real, e se a máquina virtual tem seu próprio scheduler pra gerenciar a thread-real pra rodar as green-threads temos paralelismo de verdade.</p>
<p>No Java normal não temos essas construções, mas outras linguagens por cima do Java como Scala e Clojure implementam estruturas similares de green-threads e schedulers. Em particular o Scala introduziu o framework Akka por volta de 2009, que implementa a arquitetura de Actors. Actors, é como eu chamo essa função especial no Erlang, os processos, que tem características de corotinas com múltiplos pontos de suspensão, com mailboxes que servem como filas de comunicação, e onde as mensagens trocadas entre processos não compartilham memória, nunca trafegam coisas como ponteiros, mantendo as green-threads isoladas entre si. Isso é um Actor, basicamente. Por isso tanto Erlang quanto Scala compartilham de arquiteturas similares, apesar das linguagens em si serem bem diferentes. O Jonas Bonér, criador do Akka, se inspirou diretamente no Erlang pra fazer o framework Akka do Scala, por isso eles tem conceitos similares.</p>
<p>Lembram quando eu expliquei que os schedulers de thread de Linux e o próprio suporte de pthreads eram ruins no Linux e que só depois de 2007, na kernel 2.6, que ganhamos o NPTL e o CFS do Igor Molnár? Eu expliquei que o sistema operacional mapeia uma estrutura de thread pra cada core real do hardware. O tal modelo um pra um. Mas se você adicionar um scheduler e múltiplos green-threads em user land, voltamos ao modelo M pra N que eu disse que a IBM tentou implementar no Linux com o projeto NGPT. Lembram que eu falei pra vocês guardarem essa informação? Estamos revisitando o tema agora.</p>
<p>O Go faz mais ou menos a mesma coisa. O que o Erlang chama de processo, em Go se chama goroutine. Só que eu diria que as goroutines são mais baixo nível do que um Actor de Scala. Num Erlang, assim como num Linux, cada processo tem um PID ou Process ID, um número que identifica unicamente cada processo rodando. Esse PID serve como um e-mail, e um processo pode mandar mensagens pra outro processo através desse PID. O Erlang é tão versátil que podemos mandar mensagens pra um PID de um processo em outra máquina.</p>
<p>O Go tem uma outra abstração: as channels, literalmente canais. Duas gorotinas podem compartilhar uma mesma channel. Uma gorotina escreve num channel e outra gotina pode ficar escutando do mesmo channel. Lembram de algo similar que expliquei hoje? Na prática não me parece nada muito diferente de um named pipe entre processos Unix, um canal unidirecional bloqueante. E eu disse que gorotinas e channels são mais baixo nível que Actors de Erlang ou Scala porque eles permitem trafegar qualquer coisa, incluindo ponteiros de memória, ou seja, eles compartilham memória entre si e, portanto, tem os mesmos problemas que programar com threads reais: se você escolher trafegar ponteiros, vai precisar lidar com mutexes e locks e os temidos bugs de race condition e deadlocks.</p>
<p>Diferente de Erlang ou Java, o Go não é uma máquina virtual, cheia de serviços. A filosofia e os usos são diferentes. Ele tem um runtime por causa do controle das gorotinas que podem rodar em paralelo. Diferente do scheduler de Erlang que é preemptivo, o scheduler de Go é cooperativo. Ele espera determinados eventos numa gorotina como syscalls pra receber o controle de volta e dar a vez pra outra gorotina. É menos sofisticado do que Erlang nesse caso mas é mais leve e mais previsível também. E obviamente, por causa disso ele é automaticamente mais poderoso do que Node.js e mais simples de programar aplicações complexas. Assim como Erlang ou Scala, um reactor é desnecessário, porque eles tem schedulers que podem pausar uma corotina quando alguma coisa como I/O bloqueante é executado. Na minha opinião, schedulers em user-land pra corotinas são melhores do que reactors. Reactors são interessantes em linguagens que não tem como ter schedulers em user-land. A premissa pra isso é acesso irrestrito às threads reais, sem locks globais que Python ou Ruby tem.</p>
<p>Do ponto de vista puramente dos interpretadores ou máquinas virtuais. Temos PHP, Python, Ruby, Perl e Javascript que são todos interpretadores. Alguns tem acesso a threads reais, mas na prática, todos são essencialmente single-threaded, com locks globais que chamamos de GILs por conta das antigas extensões em C. Em todos eles, a opção de um Reactor com a ajuda de Fibers pra não complicar a programação, é a melhor solução pra concorrência. Erlang e Java são máquinas virtuais, no caso de Java significa que Scala, Clojure ou mesmo Kotlin, todos rodam sobre a mesma máquina virtual. Essas VMs são excepcionais trabalhos de engenharia e foram feitas pra sequestrar todos os recursos da máquina. O melhor caso de uso é seu programa e a VM rodarem sozinhas na máquina e elas gerenciarem tudo. E mais do que isso: o melhor caso de uso é que uma vez iniciadas elas não fiquem rebootando toda hora. Guardem essa informação pro episódio de devops.</p>
<p>Erlang, na minha opinião, é quem tem as melhores abstrações de concorrência, e por consequência Elixir também já que ele basicamente gera bytecodes pra mesma VM, usando os mesmos frameworks. Em Java, Scala e depois Clojure e Kotlin adicionaram abstrações de linguagem e bibliotecas pra facilitar concorrência, inclusive no caso de Scala e Clojure implementando schedulers user-land e passando a adotar o conceito de green-threads, que o Java nativo não tem. O C# veio copiando cada uma dessas coisas de outras linguagens como Fibers, ThreadPools e tudo mais, mas que eu me lembre ele também não tem schedulers em user-land nem green-threads. Kotlin, Swift, C# tem sim classes chamadas schedulers que não é a mesma coisa que eu falei de Scala ou Go. Eles não são serviços do runtime, eles são classes pra agendar coisas pro futuro, como um cron num Linux, onde vc diz pra determinada tarefa rodar numa thread num determinado horário ou numa determinada frequência. Os nomes confundem, eu sei, mas schedulers de Kotlin, por exemplo, é como se fosse um cron.</p>
<p>O Go é uma linguagem que compila binário nativo, assim como Rust ou C. Mas o Go possui um runtime, pense um pedaço grande de código que gruda no seu pra gerar o binariozão final. Ele tem um scheduler real cooperativo e oferece green-threads com coordenação via channels, que como eu disse, parece com named pipes como eu expliquei no começo do episódio. Rust e outras linguagens como Crystal ainda Não tem o mesmo nível de concorrência e paralelismo. Mesmo em Rust o suporte a I/O assíncrono demorou pra chegar. Crystal está agora ganhando suporte experimental a rodar fibers em paralelo, mas nenhum dos dois possui schedulers em user-land que vem pré-embutido no runtime da linguagem.</p>
<p>Se não ficou claro, Javascript não tem paralelismo. Ele é a mais simples de todas as linguagens, e muito do investimento em engenharia no Google se focou em tornar a V8 muito boa em cuspir código nativo, portanto ele é um Just In Time Compiler, um JIT muito bom. Ele não tem sintaxe complicada, não tem bibliotecas padrão pra manter, e obviamente não oferece scheduler user-land também. Não tem um GIL como Python ou Ruby, mas também não tem acesso a threads reais. Sua única forma de concorrência são generators ou fibers, em multitarefa cooperativa, e na falta de um scheduler, essa multitarefa é controlada num event loop reactor. No máximo dá pra fazer fork pra outros processos. Porém, Javascript troca performance por uso de memória, então Copy on Write do Linux Não ajuda muito porque ele faz JIT, então muita coisa não pode ser reaproveitada entre processos. Por isso eu tendo a dizer que apesar de ser rápido, é uma das linguagens mais rudimentares que temos.</p>
<p>Eu ia falar sobre a parte da programação, mas como já tá comprido pra caramba de novo vou só resumir. Eu expliquei até agora sobre como as coisas funcionam por baixo. Agora como tudo isso fica no seu código? Se lidarmos com threads reais, já expliquei que precisamos ficar programando mutexes pra lá e pra cá. Em linguagens antigas como Java ou C#, que expõe Threads reais, você ainda precisa se preocupar com isso. Em Go também você precisa se preocupar com isso, porque ele transporta ponteiros nos channels entre gorotinas. Ou seja, o oposto de um Erlang ou Elixir que não compartilha nada entre seus processos. Portanto ficou na sua mão como desenvolvedor se virar com sincronização e coordenação. Experimente criar um deadlock no seu channel de Go: seu programa crasheia. É um dos motivos de porque eu fico decepcionado com Go em 2019 que já poderia ter oferecido abstrações melhores direto na linguagem. Mas é uma opinião pessoal, o mesmo problema acontece num C ou C++ onde eu tenho acesso a tudo, incluindo ponteiros. Esconder ponteiros é uma decisão de design da linguagem. Mas assim como GOTO desapareceu, Null é considerado ruim hoje em dia, Ponteiros também já haviam sido aposentados quando Java foi criado, não é nenhuma novidade.</p>
<p>De qualquer forma, Node.js no começo, por 2010, tornou famoso o estilo porco de programação com callbacks. Twisted e JQuery tornaram famosos a abstração de Deffereds. E Scala, não lembro se foi o primeiro, mas foi um dos que ajudou a popularizar o termo Future. Não deixa de ser uma pequena evolução sobre Deferreds. Na prática é mais uma forma de encapsular uma execução futura num objeto, um tipo de placeholder. Em vez de fazer sua variável receber direto o resultado de uma chamada assíncrona ou mesmo uma chamada externa tipo uma chamada HTTP, que não se tem certeza se vai completar, ou uma query num banco, você faz a variavel receber um Future e o Future encapsula essa chamada incerta. Quando o Future executar e resolver, ele é substituído pelo resultado.</p>
<p>Ninguém mais usa os nomes Deferred ou Futures ou mesmo Delay, o próximo passo nessa evolução são Promises, em particular o famoso spec A+. Se você está em Javascript faz alguns anos, já deve estar de saco cheio de ouvir falar de Promises. Dois ou três anos atrás eu dizia que a ironia de Promises é que eles permaneciam sendo promessas. Enfim, na prática, Promise é como eu já falei antes de Deferred e Futures, um objeto que encapsula alguma chamada incerta. Esse objeto é encadeável e customizável e costuma ter métodos como then e catch, que é onde você adiciona callbacks, como o addCallback do Deferred de Twisted. Java 8 tem CompletableFuture, frameworks como Guava tem SettableFuture. Javascript tem Promise/A+.</p>
<p>Deffereds, Delays, Futures, Promises, são termos que acabam sendo usados um no lugar do outro, mas hoje em dia as pessoas falam mais de Promises. Sobre os objetos de Promises podemos adicionar o que chamamos de syntactic sugar, ou literalmente um açúcar na sintaxe da linguagem pra não ter que lidar com objetos de Promise diretamente nem essas configurações via then ou catch. Se não me engano a Microsoft desenvolveu a sintaxe de async/await pro C# e várias outras linguagens implementaram algo similar.</p>
<p>Voltando um pouco, em linguagens que tem Threads costuma também existir a função de join. Por exemplo, você pode criar um array ou lista com 10 Threads. Ao criar todas elas, o código pai que está criando pode chamar Join em todas elas. Assim o programa principal vai ficar bloqueado até todas elas executarem e retornarem. Daí ele continua a execução com o resultado de todas as threads, que garantidamente já acabaram de rodar. Async e Await é quase a mesma coisa, funções marcadas como async costumam devolver um objeto Promise e o await é como se fosse um join numa thread, ou um yield num Fiber, é tipo um join numa promise, esperando que ela resolva pra depois continuar.</p>
<p>A diferença é que async/await é uma semântica que pode ser usada em threads, green-threads ou corrotinas ou fibers. Então é como se fosse Join pra coisas que não são threads. Todo programa que você escreve são trechos de código que chamam outros trechos de código, uma função chamando outra. Pra explicar um exemplo, vamos chamar a primeira função de Maria e a segunda função de Joao. Maria chama Joao, Joao executa, e devolve controle pra Maria. Se Joao for uma Fiber, Maria chama Joao, Joao começa a executar e dá yield, suspende e devolve controle pra Maria. Agora Maria pode fazer outra coisa e, se quiser, pedir pro Joao resumir de onde parou.</p>
<p>Com Promises é assim: Maria chama Joao, Joao não faz nada e devolve uma promessa. Maria pode escolher quando cobrar a promessa. Daí ela cobra, e Joao executa o prometido e quando termina “resolve” devolvendo o resultado. Agora Maria pode escolher o que fazer com o resultado. Ela tem duas opções, ou usar a sintaxe padrão de Promises e dizer ao Joao: Joao quando você terminar chama a Camila. Ou, a Maria pode cobrar a promessa do Joao, esperar ele terminar que é o await, e quando terminar ela mesma pode chamar a Camila. Entenderam, é uma questão de estilo de código, ambos vão chegar no mesmo resultado, mas hoje em dia se tornou consenso que se precisar fazer uma chamada externa, por exemplo uma requisição de rede, que não sabemos quando vai ficar pronto ou mesmo se vai completar, então englobamos num Promise. Daí escolhemos se esperamos já ele terminar ou se só atrelamos nele um callback e vamos fazer outra coisa.</p>
<p>Processos, threads, user-land e kernel-space, context switching, schedulers de user-land, corrotinas, fibers, generators, são construções do sistema operacional, interpretador, runtime ou máquina virtual pra modelar e executar concorrência e paralelismo. Recursos das linguagens como filas, mailboxes e channels, promises e async/await são recursos das linguagens pra facilitar a programação de concorrência e paralelismo.</p>
<p>Finalmente, chegamos ao final. Espero que tenham aprendido o suficiente pra começar a entender como as diferentes linguagens lidam com concorrência e paralelismo, o que de fato acontece por baixo dos panos, e não misturem o que uma linguagem pode ou não pode fazer. Na prática, hoje em dia, todas as linguagens tem recursos muito bons. Alguns servem pra coisas que os outros não são tão bons, então nenhuma das linguagens que falei aqui é obrigatoriamente melhor do que a outra. Não adianta ser só rápido. Não adianta só ter concorrência massiva. Uma boa linguagem, primeiro, precisa de um ecossistema que gera resultados com eficiência. Quanto menos desperdício, quanto mais resultados, pra determinado caso de uso, melhor.</p>
<p>Estamos já faz muito tempo nesse assunto de back-end e o assunto de concorrência se esticou bastante. Ainda estou em dúvida se semana que vem ainda adiciono mais um tema, que é relacionado a gerenciamento de memória, ou se já vou direto pro assunto final da série: devops. Vou pensar ainda. Se ficou com dúvidas não deixe de mandar nos comentários abaixo, se curtiu mande um joinha, compartilhe com seus amigos, não deixe de assinar o canal e cilcar no sininho pra não perder os próximos episódios. A gente se vê semana que vem, até mais.</p>
]]></content:encoded><category>sistemas-distribuidos</category><category>linguagens-de-programacao</category><category>backend</category><category>akitando</category></item><item><title>[Akitando] #42 - Entendendo Apple, GPL e Compiladores</title><link>https://akitaonrails.github.io/2019/03/06/akitando-42-entendendo-apple-gpl-e-compiladores/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/03/06/akitando-42-entendendo-apple-gpl-e-compiladores/</guid><pubDate>Wed, 06 Mar 2019 17:00:00 -0300</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/suSvMnNwV-8"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Finalmente! Chegamos no episódio 42!&lt;/p&gt;
&lt;p&gt;Se você é geek que nem eu sabe que 42 é um número especial. Segundo o sábio profeta Douglas Adams o número 42 é a resposta da vida, do universo e de tudo. Se não acreditam leiam a bíblia do Guia do Mochileiro das Galáxias. De curiosidade é por isso que minha empresa se chama Codeminer 42. E em especial este ano eu vou completar meus 42 anos. Mas chega de numerologia, vamos começar a história de hoje.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/suSvMnNwV-8"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Finalmente! Chegamos no episódio 42!</p>
<p>Se você é geek que nem eu sabe que 42 é um número especial. Segundo o sábio profeta Douglas Adams o número 42 é a resposta da vida, do universo e de tudo. Se não acreditam leiam a bíblia do Guia do Mochileiro das Galáxias. De curiosidade é por isso que minha empresa se chama Codeminer 42. E em especial este ano eu vou completar meus 42 anos. Mas chega de numerologia, vamos começar a história de hoje.</p>
<p>Hoje vamos fugir um pouquinho do tema de Back-end, que pretendo retomar semana que vem. Desta vez quero contar uma história que eu pessoalmente acho interessante. Vou falar sobre Apple, sobre licenças de software livre, sobre compiladores, um pouco do mundo mobile e um pouco do mundo web de novo. É uma história meio complicada mas que vai ajudar vocês a entenderem um pouco da dinâmica de como o mundo real afeta a evolução e adoção de linguagens e tecnologias.</p>
<p>Errata: várias vezes no vídeo eu falo &ldquo;SpiderMonkey&rdquo; mas eu queria dizer &ldquo;JavascriptCore&rdquo;. A SpiderMonkey é a engine de Javascript do Firefox, a JavascriptCore que é a engine que vem no WebKit e Safari. E o V8 é o do Chrome.</p>
<p>Errata 2: Eu falo que GPL é General Protection License, mas na verdade é General Public License.</p>
<p>Errata 3: me avisaram nos comentários e eu realmente comi bola quando falei dos emuladores de Android. Antigamente as imagens do OS eram realmente só de ARM mas hoje em dia de fato já existem imagens compilados pra x86, então em um Virtualbox habilitado pra usar os recursos de virtualização dos processadores AMD e Intel como o VT-X é possível acelerá-los. Ainda não é a mesma coisa que no MacOS/iOS que é um simulador sem necessidade de camada de virtualização, mas não deixa de ser um avanço.</p>
<p>Links:</p>
<ul>
<li>Why WebAssembly is Faster Than asm.js (<a href="https://hacks.mozilla.org/2017/03/why-webassembly-is-faster-than-asm-js/"target="_blank" rel="noopener">https://hacks.mozilla.org/2017/03/why-webassembly-is-faster-than-asm-js/</a>)</li>
<li>Awesome Wasm (<a href="https://github.com/mbasso/awesome-wasm"target="_blank" rel="noopener">https://github.com/mbasso/awesome-wasm</a>)</li>
<li>Swift is like Kotlin (<a href="http://nilhcem.com/swift-is-like-kotlin/"target="_blank" rel="noopener">http://nilhcem.com/swift-is-like-kotlin/</a>)</li>
<li>THE CASE THAT NEVER ENDS: ORACLE WINS LATEST ROUND VS. GOOGLE (<a href="https://www.wired.com/story/the-case-that-never-ends-oracle-wins-latest-round-vs-google/"target="_blank" rel="noopener">https://www.wired.com/story/the-case-that-never-ends-oracle-wins-latest-round-vs-google/</a>)</li>
<li>Facts and effects of Caldera/SCO vs IBM (<a href="https://www.computerweekly.com/feature/Facts-and-effects-of-Caldera-SCO-vs-IBM"target="_blank" rel="noopener">https://www.computerweekly.com/feature/Facts-and-effects-of-Caldera-SCO-vs-IBM</a>)</li>
<li>A new GCC runtime library license snag? (<a href="https://lwn.net/Articles/343608/"target="_blank" rel="noopener">https://lwn.net/Articles/343608/</a>)</li>
<li>A visual timeline of the Microsoft-Novell controversy (<a href="https://arstechnica.com/information-technology/2007/01/linux-20070128/"target="_blank" rel="noopener">https://arstechnica.com/information-technology/2007/01/linux-20070128/</a>)</li>
<li>The Microsoft-Novell Linux deal: Two years later (<a href="https://www.infoworld.com/article/2654097/the-microsoft-novell-linux-deal--two-years-later.html"target="_blank" rel="noopener">https://www.infoworld.com/article/2654097/the-microsoft-novell-linux-deal--two-years-later.html</a>)</li>
<li>An Introduction to Tivoization (<a href="http://www.linfo.org/tivoization.html"target="_blank" rel="noopener">http://www.linfo.org/tivoization.html</a>)</li>
<li>Linus Torvalds, Dual-Licensing Linux Kernel with GPL V2 and GPL V3 (<a href="https://lkml.org/lkml/2007/6/13/289"target="_blank" rel="noopener">https://lkml.org/lkml/2007/6/13/289</a>)</li>
<li>An insider&rsquo;s look at drafting the GPLv3 license (<a href="https://opensource.com/article/18/6/gplv3-anniversary"target="_blank" rel="noopener">https://opensource.com/article/18/6/gplv3-anniversary</a>)</li>
<li>Apple’s great GPL purge (<a href="http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/"target="_blank" rel="noopener">http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/</a>)</li>
<li>Apple Invests in LLVM, Suggests Merge with GCC (<a href="https://www.osnews.com/story/12715/apple-invests-in-llvm-suggests-merge-with-gcc/"target="_blank" rel="noopener">https://www.osnews.com/story/12715/apple-invests-in-llvm-suggests-merge-with-gcc/</a>)</li>
<li>Arquitetura LLVM (<a href="https://www.aosabook.org/en/llvm.html"target="_blank" rel="noopener">https://www.aosabook.org/en/llvm.html</a>)</li>
<li>What is LLVM? The power behind Swift, Rust, Clang, and more (<a href="https://www.infoworld.com/article/3247799/what-is-llvm-the-power-behind-swift-rust-clang-and-more.html"target="_blank" rel="noopener">https://www.infoworld.com/article/3247799/what-is-llvm-the-power-behind-swift-rust-clang-and-more.html</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Desta vez vamos dar uma pequena pausa na série Começando aos 40. Vocês já viram dois episódios falando sobre back-end e o assunto ainda não acabou, mas pra quebrar um pouco decidi fazer uma longa tangente hoje e falar sobre um tema cabeludo que envolve o mundo de software livre, a Apple e compiladores. É até difícil definir que tema é esse exatamente. Tem a ver com compiladores e linguagens mas não é só isso. Mas é uma história que eu acho fascinante e queria compartilhar com vocês.</p>
<p>Além disso hoje finalmente estou completando o episódio 42. Se você é geek que nem eu sabe que 42 é um número especial. Segundo o sábio profeta Douglas Adams o número 42 é a resposta da vida, do universo e de tudo. Se não acreditam leiam a bíblia do Guia do Mochileiro das Galáxias. De curiosidade é por isso que minha empresa se chama Codeminer 42. E em especial este ano eu vou completar meus 42 anos. Mas chega de numerologia, vamos começar a história de hoje.</p>
<p>(&hellip;)</p>
<p>Como vocês viram nos vídeos passados, acho que deu pra entender que a Microsoft foi um rolo compressor nos anos 90 empurrando tecnologia atrás de tecnologia, boas ou ruins. Depois ela foi bruscamente parada no começo do século por causa do julgamento anti-truste. E de 2001 até pelo menos 2014, quem calçou seus sapatos de certa forma foi a Apple. Sem repetir muito o que todo mundo já sabe vamos resumir: em 1999 os iMacs translúcidos de plástico colorido foram um enorme sucesso pra marcar o retorno de Steve Jobs, numa época onde a Apple estava a dias de fechar as portas e declarar falência. Em 2001 surgia o Mac OS X baseado no NextStep e BSD UNIX, também em 2001 o iPod conquistou o mundo, em 2003 o iTunes conquistou o mundo.</p>
<p>Em 2005 com a ascendência do Ruby on Rails e a preferência por Macs de seu criador fez com que a comunidade Rails e o gosto pela estética da Apple andassem lado a lado. De 2005 a 2006 a Apple migrou dos processadores PowerPC pra Intel e de repente todo mundo queria usar Macs pra desenvolvimento já que ele era um sabor de BSD UNIX. Diversas ferramentas que só existiam em Linux passaram a ser portados. Gerenciadores de pacotes como Fink, MacPorts e hoje HomeBrew começaram a surgir. E mais importante, Ruby vinha pré-instalado nos Macs, facilitando ainda mais a adoção inicial.</p>
<p>Por duas vezes a Apple não só passou ilesa como explodiu em crescimento no meio de duas grandes crises. Em 2001 ela lançou seu iPod bem na queda da Crash das Ponto Coms, um dos piores episódios da história da Internet. E em 2007 os iPhones aparecem, 1 ano antes da famosa crise econômica de 2008 que muitos diziam que poderia ser o fim do mundo, e você pode assistir em filmes recentes como o incrível The Big Short. Mesmo em meio a essa crise, em 2009 a App Store vai ao ar inaugurando a era do desenvolvimento mobile e todo mundo quer aprender Objective-C agora, validando o estilo de orientação a objetos que já era mais familiar a quem usava Ruby do que quem usava Java. A Apple forçou a evolução da Web em direção à morte do Flash, e ao HTML 5 e CSS 3 que rodavam melhor nos seus navegadores Safari. E com o nascimento do MobileMe que foi o serviço online de cloud da Apple; nasceu o framework javascript SproutCore, que depois viria a se tornar Ember, inaugurando o estilo de produtos Web que conhecemos hoje.</p>
<p>Pós crise de 2008 e 2009, todo mundo queria desenvolver produtos mais sofisticados, com linguagem visual limpa, com a usabilidade que a Apple era lendária por oferecer em seus produtos. Mesmo antes disso, enquanto o mundo enterprise estava adotando soluções J2EE como o IBM Websphere ou BEA Weblogic a própria Apple tinha uma arquitetura mais simples e mais sofisticada na forma do WebObjects que era o motor por trás do lendário e-commerce da Apple na época. Uma pena que o WebObjects nunca teve real tração. Toda essa experiência trouxe a Apple como uma das maiores influenciadoras das tecnologias da época. E isso coincidiu com o pior período da história da Microsoft pós-processo dos Estados Unidos contra suas práticas monopolistas depois da Guerra dos Navegadores, então a Apple praticamente não teve competição.</p>
<p>Voltando ao nosso tema de programação e linguagens, entenda que 2006 e 2007 foram anos interessantes pro mundo dos compiladores por três motivos: a migração da plataforma de Macs para Intel, o lançamento do iPhone e a migração no mundo de software livre da famosa licença GPL v2 para o controverso GPL v3. Esses três acontecimentos, na minha opinião, influenciaram muito o landscape de desenvolvimento de software entre os mundos comercial e de software livre, e eu acho que ainda não vimos tudo que pode sair disso até hoje.</p>
<p>No ano 2000 um novo projeto começou a aparecer como uma pesquisa, o Low Level Virtual Machine de Chris Lattner e Vikram Adve. Em resumo essa tecnologia se tornou um compilador modular que chamamos de LLVM apesar de não ser de fato uma máquina virtual como uma JVM. O monopólio dos compiladores, principalmente no mundo open source, era basicamente do GCC, responsável por compilar os códigos em C, C++ e várias outras linguagens, incluindo coisas como Fortran, e que gera o binário da kernel Linux e todas as ferramentas de licença aberta que rodam ao seu redor nas distribuições Linux. Mas o GCC é um grande monolito, com mais de 25 anos de contribuições e evoluções, complicado de evoluir, expandir e integrar. Porém com décadas de refinamento e ajustes, é definitivamente um dos melhores compiladores do mercado.</p>
<p>O LLVM foi criado pra ser diferente, ele separou o processo de compilação em 3 grandes estágios, front-end, otimização e back-end. Não confundir com as carreiras de front-end e back-end. Pense na entrada do compilador que é o front, o miolo, e a saída do compilador que é o back, Vale a pena eu explicar o que isso significa. Em resumo, o front-end pega o seu código fonte, digamos em C, e faz a checagem e o parsing. O resultado desse estágio é seu código limpo e traduzido em outra linguagem intermediária, chamada literalmente de Intermediate Representation ou IR. Não seria muito diferente do Java gerando bytecodes ou do .NET que gera em IL pro CLR que é literalmente Intermediate Language. Também meio parecido com o que um transpiler de Coffeescript ou Dart ou Typescript faz transformando em Javascript por baixo. Na sequência esse código em IR passa pelo tal miolo, a fase de otimização, que vai fazer todas aquelas otimizações que eu expliquei no episódio anterior pra deixar seu código o mais eficiente possível. Inclusive como o GCC e outros compiladores, essa fase pode precisar “passar” pelo código múltiplas vezes, você às vezes vai ver esse termo “passes” porque algumas otimizações exigem conhecimento do código anterior. Finalmente o LLVM pega esse sub-produto otimizado e passa pro back-end, que vai finalmente traduzir em binário de máquina, gerando o executável do seu programa. Mas o importante é entender que existem 3 grandes pedaços de software independentes que, juntos, formam o compilador.</p>
<p>Então, em vez de usar GCC pra compilar C, você pode substituir por Clang que é um front-end de C feito pra LLVM. Teoricamente hoje você poderia tirar o GCC e recompilar uma distribuição Linux inteira usando Clang, até existem projetos testando isso. Por muitos anos, fazer o LLVM cuspir um binário tão otimizado quanto o de GCC foi bem difícil. E também havia a questão: pra que eu quero substituir o GCC se ele já funciona tão bem?</p>
<p>Em 2005 a Apple fez um de seus movimentos mais interessantes e pouco comentados e contratou Chris Lattner, que na época estava em início de carreira na Microsoft Research. Em 2006 Steve Jobs sobe ao palco na conferência WWDC e anuncia a transição da plataforma Mac dos processadores IBM PowerPC para Intel, mais especificamente a nova linha de processadores Intel Core. O sistema operacional OS X 10.4 Tiger foi o primeiro compilado ainda com GCC pra rodar tanto em PowerPC quanto Intel.</p>
<p>Mas em 2007 teríamos a sequência com o OS X 10.5 Leopard e o XCode 3.1, a IDE de desenvolvimento pra Mac e junto com ela viria o compilador GCC 4.2 e junto o LLVM integrado ao code generator do GCC, que foi conhecido como LLVM-GCC. O XCode 4.1 da época do Mac OS X Lion de 2011 foi o último a vir com o GCC como compilador, e de lá em diante o desenvolvimento em MacOS e iOS depende só do Clang. Ou seja, de 2006 até 2008 os produtos com processadores PowerPC foram todos migrados para Intel. Mas mais importante que isso, o compilador GCC foi sendo gradativamente substituído por Clang, a partir de 2007 até pelo menos 2011. Guarde essa informação.</p>
<p>No começo do século um novo tipo de produto começou a fazer um sucesso estrondoso. Lembra quando eu contei qual era o objetivo original do Java quando ele foi criado no começo dos anos 90? Ser um sistema pra um hardware de set-top-boxes. Ele estava quase uma década adiantado porque foi só nos anos 2000 que esse tipo de produto realmente explodiu, em particular o Tivo. Aqui no Brasil não tivemos Tivo mas tivemos similares. Era aquele produto que você podia gravar programas de TV a cabo pra assistir depois. É o que se chamava de DVR ou Digital Video Recorder, basicamente um receptor de sinal analógico, que digitalizava o vídeo e gravava organizado num HD. Acho que até hoje algumas empresas de TV a cabo ainda oferecem esse tipo de produto. Mas nos Estados Unidos o Tivo teve um sucesso explosivo. Tecnicamente falando ele era um mini-pczinho com uma placa de captura de vídeo que rodava uma versão de distribuição Linux.</p>
<p>Não é hoje que vou entrar em detalhes sobre licenças de software livra, mas a kernel Linux e a maioria das ferramentas que compõe uma distribuição que forma um sistema operacional completo, as ferramentas GNU, o compilador GCC e tudo mais usavam a licença GPL v2. GPL que significa General Protection License. Na prática, qualquer um, especialmente qualquer empresa que modifique código licenciado via GPL é obrigado a republicar as modificações ao público. O GPL não é uma unaminidade, por isso existem outras licenças, mas a maioria das coisas num Linux costumava ser GPL. E foi o que a Tivo fez: ela seguiu a licença e liberou as modificações que fez. Isso inclusive permitia que hobistas pudessem baixar esse código, fazer suas próprias modificações e carregar no Tivo. Então era como se fosse um PCzinho que dava pra brincar e instalar seus próprios programas.</p>
<p>Pelo menos naquela época, a maioria das empresas não gostava que seus dispositivos fossem modificados pra fazer coisas que não deveriam. Existem motivos pra isso, e nem vou entrar no mérito de certo ou errado, vamos só entender que eles não gostavam. Então na série 2 eles adicionaram um hardware que validava a assinatura do binário. Eu não sei como exatamente eles faziam isso, mas se você é iniciante vale entender pelo menos o conceito básico. Existe uma coisa em computação que chamamos de hashing. Existem algoritmos como os antigos MD5 ou SHA-1 ou os atuais SHA-256 ou bcrypt. Sem entender de criptografia entenda que eles são funções. O argumento de entrada pode ser qualquer bloco de dados, seja texto ou seja um binário. E a saída é sempre um texto de tamanho fixo.</p>
<p>Isso não é encriptação. Quando se encripta alguma coisa, você consegue reverter o processo e desencriptar. Em hashing não existe forma de pegar o resultado e reverter o processo e conseguir o argumento original. Dois blocos de dados diferentes podem dar o mesmo resultado, isso seria o que chamamos de colisão, mas é raro que isso aconteça num cenário real. O importante é que esse resultado serve como uma impressão digital, um fingerprint, de seja lá qual dado você tenha passado pra função. Ou seja, digamos que você passe o binário do kernel Linux. E o Tivo tem um firmware separado que quando carrega a kernel passa o binário por essa função e compara com a impressão digital que ele tem guardado num hardware que você não pode modificar.</p>
<p>Ou seja, se você modificou o kernel e instalou lá, a impressão digital vai ser diferente, então o Tivo pode rejeitar esse novo binário e impedir o boot. Essa seria uma forma simplória de fazer isso. Existem formas mais sofisticadas mas esse exemplo deve ajudar a entender parte do processo. Isso seria um tipo de DRM ou Digital Rights Management que a indústria de música adorava. Mas a Tivo achou um loophole no GPL v2, uma brecha na licença. A licença obrigava a liberar o código modificado, e isso eles fizeram, mas ela não fala nada sobre impedir o hardware de rodar código modificado no seu hardware.</p>
<p>Isso foi uma controvérsia gigante porque esse sistema de DRM, do ponto de vista dos desenvolvedores, restringia a liberdade do que o consumidor poderia fazer com o hardware que ele comprou. Isso gerou uma enorme polarização na opinião se uma empresa podia ou não fazer isso. Richard Stallman e Eben Mogler, os criadores da licença GPL ficaram enfurecidos, porque a GPL v2 não conseguia proibir essa atitude. Então eles iniciaram a modificação da GPL v2 para o novo GPL v3 que adicionava cláusulas que impediria isso que eles chamaram de “Tivoização”. E se você acha que o mundo open source apoiou essa medida, você está enganado. Isso gerou uma nova controvérsia e uma nova polarização. Por exemplo, Linus Torvalds odiou a medida e a kernel do Linux permanece na GPL v2.</p>
<p>Como eu já havia explicado no episódio da Lerna vs a ICE, licenças de software livre podem ser divididas em duas grandes categorias: as licenças restritivas e as licenças permissivas. Ao contrário do que o senso comum pode imaginar, o GPL é uma licença restritiva. Licenças como BSD, MIT, Apache e outras são mais permissivas. Em última instância a GPL impõe uma restrição importante: você é obrigado a liberar qualquer modificação que fizer num código GPL e essa modificação também será GPL. E sem cláusulas especiais e sub-licenças como LGPL você teria que se preocupar inclusive, se o binário gerado por um compilador com licença GPL se ela herda o GPL e se pode ser usada livremente ou não. É por isso que inclusive muitos chamam a GPL de uma licença viral. Já código licenciado via BSD por exemplo, não tem essa restrição. Você pode modificar o código e não tem a obrigação de liberar a modificação. Você pode gerar um binário e comercializar o binário e não se preocupar em ter que liberar o código.</p>
<p>A discussão sobre licenças de software livre é em última instância uma discussão sobre propriedade intelectual. A maioria das empresas prefere ficar bem longe de licenças restritivas pra não comprometer suas propriedades intelectuais, que é o que garante o seu valor. Nos anos recentes no começo dos anos 2000 tivemos grandes casos controversos em torno de propriedade intelectual no mundo open source como o famoso caso da SCO ou Santa Cruz Operation que processou a IBM pedindo nada menos que 1 bilhão de dólares, alegando que eles infringiram na sua propriedade do UNIX e usaram esse conhecimento nas contribuições ao Linux que a IBM fazia.</p>
<p>Se a SCO tivesse ganhado isso teria sido uma bomba nuclear tornando o Linux material radioativo pra quem usasse. Mas numa virada de eventos um juiz determinou que quem possuía os direitos que a SCO clamava na verdade era a Novell. E nessa mesma época por 2005 ou 2006 a Microsoft fechou um acordo de 400 milhões de dólares com a Novell que era dona do Suse Linux, cujo combinado era que uma não iria processar a outra em torno de propriedade intelectual, meio que sugerindo que haveria alguma coisa no Linux que deixaria quem não usasse Suse Linux vulnerável a algum processo no futuro de alguma forma. A comunidade open source odiou esse movimento, claro.</p>
<p>Certo ou errado, vocês podem entender pelo menos porque levar propriedade intelectual e licenças a sério é importante, principalmente nos Estados Unidos. À medida que as empresas iam adotando tecnologias open source, essa dúvida sempre deixa todos com os nervos à flor da pele. Se o Linux, que é a kernel por baixo de todas as distribuições e que estava sendo adotado rápido por todos, fosse condenado num processo desses, ele se tornaria material radioativo e toda empresa usando poderia descobrir uma bomba relógio explodindo. Isso é realmente sério. Se nos anos 2000 alguém perguntasse porque a Apple preferiu usar a base do FreeBSD nos OS X em vez do Linux, esse certamente poderia ser considerado um dos principais motivos: o FreeBSD usa licença BSD que é bem permissivo, ao contrário da GPL que tinha todas essas dúvidas pairando no ar.</p>
<p>Agora, o GPL v3 é um problema sério. Que outra empresa além da Tivo prefere manter certos códigos escondidos e dificultar você de instalar o que quiser em seus hardwares? Eu sempre fui fã da Apple e certamente a integração de hardware e software que eles fazem é excepcional, mas se você pensar puramente em questão de software livre, você vai ficar no mínimo em cima do muro com as práticas da Apple. E não confunda, sim, a Apple tem o código da base do seu sistema operacional aberto ao público na forma do Darwin, mas o MacOS é bem mais que só o Darwin, tem centenas de componentes que eles não querem abrir, incluindo a forma como o sistema se integra perfeitamente nos Macs e Macbook e, claro, nos iPhones de hoje. Se você notou que um iPhone faz a mesma coisa que um Tivo, impedindo você de instalar outro sistema nele, você pensou certo.</p>
<p>No outro lado do espectro, além de distribuições baseadas na kernel Linux, existem as distribuições baseadas em BSD, como FreeBSD, OpenBSD, NetBSD. O Linux é como se fosse um clone de UNIX, mas os BSDs em princípio são herdeiros do UNIX original. BSD seria o concorrente da outra linhagem de UNIX, o System V que deriva da época do UNIX System III da AT&amp;T. Como o próprio nome diz, a filosofia dessas distribuições é Software Livre no sentido mais permissivo e liberal, com a licença BSD. Vamos dizer que eles “toleram” a GPL mas nunca gostaram dela e o GPL v3 foi uma afronta direta e ofensiva pra eles, e podemos dizer que eles não gostaram nada das cláusulas restritivas anti-Tivoização. Existe uma longa história de brigas políticas em torno do que é considerado Software Livre ou não e vou parar por aqui pra não me alongar demais por hoje.</p>
<p>O maior problema? Tudo no mundo open source era compilado com o GCC. Como eu já expliquei no episódio sobre a Lerna vs a ICE, licenças não podem ser mudadas retroativamente, só em novas versões. Como quem controla o GCC é o Richard Stallman, ele migraria pra nova GPL v3. Ou seja, quem não quisesse adotar a nova licença ficaria preso na versão 4 do GCC que ainda seria GPL v2. Então começou uma corrida contra o relógio. Você pode ficar usando o GCC que ainda era GPL v2 mas quanto mais tempo passa, mais ele fica defasado e até inseguro, já que correções e novas funcionalidade só iriam subir no GCC com GPL v3.</p>
<p>Resumo da história? Tanto FreeBSD quanto a Apple começaram um projeto que iria levar alguns anos pra sair do GCC e migrar a compilação de tudo pra usar LLVM e Clang, que era o único concorrente viável ao GCC. Se você alguma vez já se perguntou porque no MacOS vem um Bash tão antigo? Porque além do GCC outras ferramentas GNU mudaram pro GPL v3, o Bash foi um deles. Por isso hoje se instala o Bash mais novo e outras ferramentas com versões mais novas, como o próprio Git, você mesmo via Homebrew ou de outra forma manual como baixar os códigos fonte e gerando binários você mesmo. E por isso às vezes você tem confusão de compilar usando GCC ou Clang.</p>
<p>Pra Apple, além da questão das licenças, a linguagem Objective-C que é o coração dos frameworks de aplicativos nativos de MacOS e iOS nunca teve prioridade no grupo de desenvolvimento do GCC. Por causa disso o projeto Clang começou a avançar a passos mais largos e por isso eles começaram a estratégia de primeiro começar a usar GCC como o front-end pra LLVM, já que a primeira coisa mais difícil era parsear corretamente os códigos em C, C++ e Objective-C e por isso você começa com LLVM-GCC como eu disse antes. À medida que a compatibilidade do front-end entre GCC e Clang ficou mais próxima, daí eles começaram a colocar Clang como escolha padrão até finalmente remover completamente o GCC antigo.</p>
<p>Nesse período de 2006 então a Apple estava migrando seus sistemas operacionais de PowerPC pra Intel e ao mesmo tempo já estava planejando outro dispositivo pra ser lançado em 2007: o primeiro iPhone. E os iPhones não usam processador Intel e sim ARM. E os iPhones seriam sempre produzidos com o bloqueio de DRM pra impedir outro sistema de rodar no hardware, ou seja, eles seriam Tivoizados. E com todos os aplicativos desenvolvidos usando Objective-C e com a necessidade de compilar binários nativos pra Intel e para ARM.</p>
<p>Agora vamos entender outra coisa do LLVM que eu não disse antes. O LLVM é um conjunto modular de bibliotecas. O compilador Clang é um conjunto dessas bibliotecas. Como ele foi desenhado pra ser modular, significa que você teria relativa facilidade de trocar partes do front-end ou do back-end. Por exemplo, você pode fazer back-ends que cospem binários em Intel ou PowerPC ou ARM ou qualquer outro tipo de hardware. E como o front-end também é modular e ele cospe em IR antes de passar pro módulo de otimização, você também tem facilidade de integrar no XCode pra fazer análise estática do código e debug. Com a integração de ferramentas sofisticadas como o famoso DTrace do antigo Solaris e a substituição do debugger GDB que era usado com GCC pelo LLDB, a IDE do XCode foi ficando mais e mais avançada para conseguir instrumentar o código e otimizar muito melhor do que antes.</p>
<p>Ao mesmo tempo que Chris Lattner e sua equipe iam evoluindo o LLVM dentro da Apple, muitas das pesquisas geraram novas funcionalidades primeiro no próprio Objective-C como a introdução de lambdas e o ARC que é o Automatic Reference Counting. Pra você que é iniciante, na maioria das linguagens mais modernas você não pensa muito em gerenciar memória porque linguagens como Java tem garbage collectors, ou seja, o runtime gerencia a memória por você. Mas se você começou a aprender com linguagens como C, sabe que precisa você mesmo codificar o gerenciamento de memória manualmente. Ou seja, no seu código você chama funções como malloc e free e outros pra pedir memória pro sistema operacional e depois devolver essa memória. Lembra que eu falei que além de multi-thread o outro aspecto em programação mais difícil é gerenciamento de memória?</p>
<p>Pois é, à medida que sua aplicação fica complexa, você vai errar isso. Ao longo da sua carreira você vai ficar ouvindo falar de vazamento de memória por exemplo. Gerenciar memória é complicado, porque memória se fragmenta, você precisa lidar com coisas como regiões, arenas e muitos outros detalhes. Mas se você tem uma máquina potente, digamos, com 8GB de RAM ou mais, você fica preguiçoso e não percebe vazamentos porque tem memória sobrando. Pra facilitar se falta memória RAM o sistema operacional ainda tem a opção de usar o HD como memória. É isso que se chama de SWAP. Se você chega nesse ponto com uma aplicação mal feita que vaza memória, tudo bem, o sistema vai passar a usar HD. Tudo vai ficar lento pra caramba, mas não vai crashear. Mas pense em 2007 um iPhone com meros 128MB de RAM pra comportar o sistema operacional e os aplicativos e sem a opção de um HD pra fazer swap. Ou seja, se faltar memória o sistema vai crashear o aplicativo. Então gerenciar memória se torna especialmente importante em dispositivos com poucos recursos.</p>
<p>E como Objective-C é basicamente compatível com C e usa a mesma semântica de você manualmente codificar onde quer alocar memória e onde quer devolver essa memória, os primeiros aplicativos pra iOS que eram feitos por amadores ou mesmo por descuido acabavam tendo muitos vazamentos e crasheavam dando uma má experiência aos usuários. Por isso recursos como o ARC que foram introduzidos ao Objective-C eram importantes, pra evitar que você precise manualmente escrever código pra gerenciar memória, além da introdução de ferramentas como o DTrace e LLDB pra instrumentar seu código e conseguir analisar exatamente onde estão ocorrendo vazamentos, pra que você consiga consertar antes de mandar o binário final pra App Store.</p>
<p>E como desenvolvedor você ainda tem outro problema grave. Se você já tentou desenvolver pra Android deve ter notado que se quiser testar seu código antes de instalar no celular, a única forma de rodar no seu computador é usando uma máquina virtual. Não estou falando da JVM mas de máquinas virtuais de verdade como QEMU, VirtualBox ou VMWare. Primeiro porque o Android é um sistema operacional baseado em Linux e os binários estão pré-compilados pra processadores ARM. E binários de ARM não são compatíveis com processadores Intel que roda na sua máquina de desenvolvimento, seja PC ou Mac.</p>
<p>A única forma então é usar um emulador, que é uma máquina virtual, que vai traduzir as instruções de ARM em instruções de Intel. Quando você ouve que seu novo Samsung S10 ou mesmo Xiaomi Mi Mix rodam Qualcomm, você sabe que é um processador ARM. Então você programa seu aplicativo pra Android e sobe um emulador de Android como o da Bluestacks ou Nox ou qualquer outro no seu PC e ele vai emular o Android fazendo ele pensar que está rodando numa máquina ARM. Por isso é tudo tão absurdamente lento pra testar seu código, porque emular exige essa tradução de instruções e isso é lento. Não tem jeito.</p>
<p>Mas se você já desenvolveu pra iOS em Macs sabe que a mesma coisa não acontece quando você pede pra testar seu novo app de iPhone no seu Mac. Seu app roda com velocidade total, sem nenhuma lentidão. E você vai notar no XCode que quando vai rodar seu app no seu Mac de desenvolvimento, não existe uma opção pra subir um Emulador, como no Android, e sim um Simulador. A Apple fez uma coisa difícil mas que se pagou: eles compilam o sistema operacional iOS que roda em iPhone e iPad pra binários de Intel de verdade. Então quando você sobe o simulador, ele não precisa de uma máquina virtual pra traduzir as instruções, ele já é binário de Intel! E quando você compila seu projeto pra rodar no simulador o XCode está usando o LLVM pra cuspir binários de Intel também. Só quando você manda empacotar pra publicar na App Store que ele vai compilar em binário de ARM. Parece uma coisa meio óbvia de fazer, mas fazer isso da forma quase transparente como o XCode faz é impressionante até hoje dado que as ferramentas de desenvolvimento de Android do Google até hoje não fazem isso.</p>
<p>Por outro lado, alguns podem argumentar que apesar das funcionalidades impressionantes que a Apple conseguiu colocar no Objective-C, ele ainda tem a sensação de ser uma linguagem com sintaxe muito velha, especialmente se comparado ao Android onde o Google resolveu copiar a linguagem Java, embora similar ao J++ da Microsoft nos anos 90, ele também não é compatível com a JVM de verdade. O runtime do Android, o Dalvik é diferente. De qualquer forma, já discutimos como a linguagem Java também tem sintaxe considerada meio velha se comparada a coisas como Python ou Ruby ou mesmo Groovy. Mas se comparar com Objective-C - que eu pessoalmente gosto - a maioria vai dizer que prefere Java por estar mais acostumado e ter visto na faculdade.</p>
<p>Enquanto evoluía o LLVM, o Clang, os frameworks de iOS, as tecnologias do XCode, Chris Lattner começou a pensar numa nova sintaxe de linguagem. Assim em 2010 surgia o Swift, uma linguagem que é extremamente agradável ordens de grandeza se  comparado ao Objective-C. E aqui surge outra enorme vantagem do LLVM. Assim como você pode ter back-ends que cospem binários de Intel ou ARM, você também pode ter múltiplos front-end. Você já tinha o front-end que entendia Objective-C, então eles fizeram um novo front-end pra essa nova linguagem Swift. E qual a vantagem? Os binários de ambos Objective-C e Swift são compatíveis entre si. Então seria super fácil você migrar pra Swift e continuar consumindo as mesmas bibliotecas e frameworks que já existiam e eram feitos em Objective-C. Você não precisa reescrever tudo em Swift, pode migrar aos poucos.</p>
<p>Então no mundo de Mac e iOS você tem agora a escolha de usar Swift com sua sintaxe bem mais moderna inspirada em Ruby, Python, Groovy e outras linguagens, com construções modernas, com o ARC pra gerenciar memória e ter a facilidade de rodar sua aplicação iOS em velocidade máxima no simulador de iOS com tudo compilado em binário de Intel e depois facilmente empacotar em binário de ARM pra rodar no seu iPhone de verdade. Por isso muitos desenvolvedores preferem criar apps pra iOS antes de Android até hoje.</p>
<p>No mundo Android as coisas não eram muito agradáveis. O Google também teve sua parcela de dor de cabeça com processo por propriedade intelectual. Lembram que eu já falei várias vezes que a Sun tinha sido adquirida pela Oracle? Então, eles foram processados por usar Java e a Oracle veio atrás com força. Oito anos depois o processo ainda não acabou e em 2018 a Oracle estava ganhando a batalha. Mas todo esse stress não faz o Google ter muito amor pelo Java. Ao mesmo tempo eles decidiram começar a mudar o foco do Java pra outra linguagem, no caso o Kotlin.</p>
<p>No Android o processo é o seguinte: você escreve seu código em Java e pode usar um compilador normal pra gerar bytecodes Java. Por isso você pode também usar muitas bibliotecas Java que já existem. Daí o bytecode Java é traduzido num segundo conjunto diferente de bytecodes chamado DEX que é o que roda no Dalvik. Antes esse DEX era interpretado no Dalvik, e tinha mais ou menos as mesmas funcionalidades que uma JVM como um Just-in-time compiler ou JIT pra gerar o binário nativo de ARM à medida que era executado. Como eu também já disse, isso é bacana pra aplicativos que rodam num servidor que fica de pé por dias e dias. Mas num dispositivo móvel que você abre um aplicativo e fecha a qualquer momento, um JIT ajuda mas não tanto assim, porque ele não tem tempo pra otimizar como poderia. Fora que rodar um compilador JIT também consome processamento, e isso consome bateria, que é outra coisa que sempre falta em dispositivos móveis.</p>
<p>Então nas versões mais recentes o bytecode DEX é pré-compilado em binário nativo antes de rodar, esse é o processo que você vai ouvir falar como AOT ou Ahead-of-Time compiler. Que é o que um compilador normal como GCC ou Clang fazem: geram binários nativos. O processo de desenvolvimento no Android tem mais passos pra poder ter essa opção de compatibilidade com o bytecode Java, que foi uma jogada importante pra conseguir trazer programadores Java pro mundo Android sem precisar ensinar uma nova linguagem pra todo mundo.</p>
<p>Mas ao mesmo tempo temos a Oracle baforando no cangote do Google e a essa altura já disse várias vezes que Java é uma linguagem que já não é mais considerada agradável de trabalhar e nem tão produtiva se comparado a outras linguagens como o novo Swift. Então o Google foi bater na porta de outra empresa, a JetBrains, que a anos faz uma das melhores IDEs comerciais pro mundo Java, e em 2011 havia começado a criar uma nova linguagem, que assim como Groovy ou Scala ou Clojure também compilam em bytecode compatível com Java. Pro Google foi uma escolha fácil porque eles negociaram pra liberar as ferramentas da JetBrains de graça pra desenvolvedores Android e a evangelizar a nova linguagem deles, que é chamado de Kotlin. Kotlin que é finalmente uma linguagem moderna mas familiar e ser ser exótica demais como Clojure.</p>
<p>Então no mundo mobile, em iOS você ainda pode usar Objective-C que é considerado uma linguagem “velha” mas agora a maior parte do desenvolvimento é na linguagem Swift que é bem mais moderna e produtiva. Mesma coisa no mundo Android onde você ainda pode usar a linguagem Java e as bibliotecas Java que também já é considerado meio “velho” por muita gente mas também pode migrar pro Kotlin que está ainda em adoção por toda a comunidade Android. Você pode até misturar código Kotlin e Java no mesmo projeto. Na verdade, por coincidência, seja porque ambas se inspiraram em fontes parecidas na linhagem de linguagens, Swift e Kotlin tem sintaxes até que similares. Dependendo do código que você ver, pode até quase confundir um pelo outro. Claro, as bibliotecas de iOS e Android são bem diferentes, mas as duas linguagens são igualmente modernas e eu pessoalmente diria que são as melhores até agora para o mundo geral de programação de aplicativos, especialmente se comparar com Java ou C#.</p>
<p>Mas deixando o mundo mobile um pouco de lado, um dos resultados de tudo isso foi a evolução rápida do LLVM enquanto uma solução robusta pra desenvolvimento de compiladores. Em vez de ter que mexer no monolito complicado que é o GCC e ainda ter que lidar com a política da licença GPL v3, você pode ir pro LLVM e foi isso que muita gente fez, menos o Google. Pra ser justo, o Google contribui pro LLVM também mas não usa tão extensivamente quanto a Apple. A linguagem Go eles preferiram criar o compilador do zero, primeiro em C e depois traduzindo o código C no próprio Go. Lembra que eu falo que o Google tem a péssima mania de ficar reinventando a roda? A gente chama isso de NIH ou síndrome de Not-Invented Here, muitos programadores sofrem dessa síndrome de prima-donna de achar que pode fazer melhor que os outros.</p>
<p>Mas já sabemos que C/C++, Objective-C e Swift tem front-ends pra LLVM e sabemos que ele tem back-ends que geram binários pra Intel e ARM. E sabemos que ele tem um design mais moderno, mais modular e com ferramentas mais modernas pra desenvolvedores como o LLDB  que muitos consideram melhor do que o antigo GDB. E ele ficou muito bom em otimizar o IR pra gerar binários nativos quase tão performáticos quanto o GCC. Isso abre uma oceano de possibilidades. E se decidíssemos criar mais front-ends pra outras linguagens?</p>
<p>E foi isso que a comunidade open source começou mesmo a fazer. Hoje temos front-ends de LLVM pra ActionScript, Ada, C#, Common Lisp, Crystal, CUDA, D, Delphi, Fortran, Graphical G, Halide, Haskell, bytecode Java, Julia, Kotlin, Lua, Objective-C, a linguagem de shading de OpenGL, Pony, Python, R, Ruby, Rust, Scala, Swift, e Xojo.
Mas claro, algumas estão mais avançadas do que outras, algumas são mais experimentais. Algumas usam só algumas funcionalidades como o Just-in-Time compiler do LLVM que é o caso da linguagem Julia e inclusive em lugares inusitados como o banco de dados PostgreSQL que hoje também usa o JIT do LLVM pra otimizar queries complexas.</p>
<p>Uma das linguagens que você mais deve ter ouvido falar que depende de LLVM é o Rust da Mozilla. Agora você sabe o que roda por baixo do Rust e quem gera os binários. A linguagem Rust é uma das mais diferentes de todas que já mencionamos até agora. Sem entrar em muitos detalhes hoje, ela tem um foco maior no problema do gerenciamento de memória combinado com preocupação de segurança. Mas seu maior problema é o que chamamos da ergonomia da linguagem. Apesar de ser uma sintaxe moderna, ela realmente exige mais do desenvolvedor se comparado a um Swift ou Kotlin. Partes do Firefox hoje são escritos com Rust e, adivinhem, alguns evangelistas da linguagem são ex-rubistas também.</p>
<p>Outra linguagem que compila com LLVM que ainda não é muito conhecida mas que eu pessoalmente gosto bastante é a Crystal. Ela se parece muito, mas muito mesmo com Ruby já que é inspirada diretamente nela, e em alguns casos você pode quase copiar e colar trechos de código Ruby e compilar com Crystal sem mexer em nada, então a curva de aprendizado pra quem vem de Ruby é muito mais simples. Ao contrário de Rust a ergonomia da linguagem é muito melhor. Mas Rust tem características que a tornam mais adequadas como substituto de C pra desenvolver código nativo rápido e leve. O Crystal se assemelha mais ao Go, ele tem um runtime maior e um garbage collector maior também. Diferente do Go que tem o investimento infinito Google por trás, o Crystal é um esforço quase voluntário da comunidade, por isso anda mais devagar. Mas graças ao LLVM os binários que o Crystal gera são competitivos com Go mesmo nesse estágio.</p>
<p>E mesmo sem ser programador você provavelmente usa tecnologias LLVM hoje e nem sabe disso. Por exemplo tanto o SpiderMonkey que é a engine, ou motor, de Javascript do navegador Safari no MacOS e no iOS quanto o V8 que é a engine de Javascript do Chrome possuem a funcionalidade de Just-in-Time Compiler ou JIT que eu mencionei já hoje e no episódio anterior. Repetindo: Javascript é uma linguagem interpretada, como Python ou Ruby. O SpiderMonkey e o V8 recebem o arquivo de texto escrito em Javascript e fazem um parsing e começam a interpretar o código.</p>
<p>Porém como vocês já sabem e eu expliquei no episódio da História do Front-end nesta série, começou a surgir o conceito de Single Page Application, que é basicamente um site que se comporta mais como um aplicativo de desktop do que um site web normal de conteúdo. Na prática são sites onde você não fica navegando entre diversas páginas, você carrega uma página e fica nela e por sua vez ela carrega muito javascript pra controlar o comportamento. Imagine Google Docs, ou Spotify. Nesses casos as engines de Javascript, quando percebem que estão rodando faz algum tempo começam um processo que é mais devagar, que é o JIT, e começam a otimizar e compilar as partes do código Javascript mais executadas em binário nativo, que ainda depende do runtime das engines, mas é bem mais eficiente do que interpretação.  No caso, o SpiderMonkey usa o JIT do LLVM. SpiderMonkey e V8 passaram anos evoluindo e competindo pra ver quem era o melhor e acho que isso continua assim até hoje.</p>
<p>Por isso aplicativos como Spotify, Atom, Slack, Discord e outros conseguem rodar com performance aceitável. Some a isso que os navegadores por baixo hoje suportam CSS acelerado pelo hardware gráfico, as GPUs como o que já vem integrado no seu processador Intel ou placas externas como os GeForce da NVIDIA ou os Radeon e Vega da AMD. A evolução do que chamamos de GPU foi uma explosão neste século como o de CPUs foi nos anos 70 a 2000. Num determinado momento as GPUs passaram a suportar shaders, que como o nome diz, serviam pra controlar a sombra ou tonalidade das cores. Mas em breve elas passaram a ser programáveis numa linguagem similar a C. E adivinhem, podemos acelerar esses shaders compilando em tempo real, via um JIT, usando LLVM hoje em dia. Por isso se você executar um VirtualBox, a renderização gráfica via software, que é lento, hoje é compilado via JIT por um driver chamado llvmpipe. Se você lida com desenvolvimento com placas NVIDIA tem o compilador pros CUDA cores da GeForce, e a NVIDIA contribuiu os códigos de back-end pra permitir gerar shaders pras suas placas diretamente no LLVM abrindo as portas pra qualquer linguagem poder se beneficiar do processamento paralelo massivo em GPUs.</p>
<p>Agora, o último assunto de hoje pra você entender como o LLVM pode ir mais longe. O Front-end pode ser qualquer linguagem, ele cospe o IR, que é otimizado e depois passado pra um Back-end, que também pode ser qualquer coisa, por exemplo, os back-ends que geram binários de Intel ou ARM como já repeti várias vezes. Porém, vocês já viram que existem os transpilers, como o do Typescript que cospe Javascript. E se pudéssemos usar um front-end de C como o Clang e usar um back-end que cospe Javascript?</p>
<p>Esse é o projeto Emscripten. A demonstração mais impressionante se não me engano em 2014 foi quando a Epic Games pegou uma animação usando a engine de jogos Unreal e compilou com esse Emscripten, usando LLVM, de C pra Javascript e somado às capacidades de aceleração via GPUs dos navegadores via WebGL, conseguiu rodar o que antes era um programa nativo dentro de um navegador, com excelente performance graças a tudo que eu expliquei que dá pra fazer com LLVM. Pra fazer isso, eles limitaram o Javascript num sub-conjunto que ficou conhecido como ASM.js. O conceito é simples: CPUs tem instruções nativas, que podemos escrever em Assembly e usar um Assembler pra cuspir o bytecode nativo da máquina. Máquinas virtuais como a JVM tem seus bytecodes. E se considerarmos que o Javascript é um assembly da máquina virtual chamada navegador?</p>
<p>Isso abre todo um leque de possibilidades. E foi o que inspirou o projeto WebAssembly que surgiu pra formalizar esse sub-conjunto de Javascript que poderia ser o back-end pra compiladores baseados em LLVM. Hoje em dia ainda temos o Enscripten pra compilar em ASM.js e o novo projeto Brynaryen que nasceu sendo uma extensão do Enscripten mas foi reescrito do zero em C++ pra gerar WebAssembly - maldita síndrome de not-invented here, a Mozilla também sofre disso como o Google. Ainda não tivemos nenhum resultado bombástico, mas diversos grupos estão fazendo experimentos pra entender o que realmente podemos fazer. Se você procurar por emscripten vai ver demonstrações muito interessantes, inclusive uma pequena distribuição de Linux compilado inteiro com Emscripten e rodando dentro do navegador, sem emulação nem nada. Na prática, no futuro, poderíamos escrever aplicativos Web em qualquer linguagem e compilar tanto pra WebAssembly quanto pra binário nativo se quiséssemos. Mas ainda estamos no começo dessa jornada.</p>
<p>E por hoje vamos ficar por aqui, pra variar virou um episódio grande, mas eu queria contar essa história pra que você pudessem entender como política influencia a evolução das linguagens, ferramentas e arquiteturas que nós usamos hoje. Não é só uma questão se determinada linguagem é mais rápida que outra ou não. Como programador você precisa entender como diversos fatores externos e modelos de negócio e interesses políticos influenciam tudo que você usa.</p>
<p>Na semana que vem vou retornar ao tema de back-end falando sobre outro assunto que é controverso e muitos se confundem até hoje: concorrência e paralelismo e como diferentes linguagens lidam com isso de formas diferentes. Se ficou com dúvidas nesse episódio não deixe de mandar nos comentários abaixo, se curtiram mandem um joinha, compartilhem com seus amigos, assinem o canal e não deixem de clicar no sininho pra não perder os próximos episódios. A gente se vê semana que vem, até mais!</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>apple</category><category>open-source</category><category>akitando</category></item><item><title>[Akitando] #41 - Entendendo Back-End para Iniciantes em Programação (Parte 2) | Série "Começando aos 40"</title><link>https://akitaonrails.github.io/2019/02/27/akitando-41-entendendo-back-end-para-iniciantes-em-programacao-parte-2-serie-comecando-aos-40/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/02/27/akitando-41-entendendo-back-end-para-iniciantes-em-programacao-parte-2-serie-comecando-aos-40/</guid><pubDate>Wed, 27 Feb 2019 17:00:00 -0300</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/N6vgZr1k03g"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Esta é a Parte 2 do tema de Back-end para Iniciantes. Desta vez vamos olhar com mais cuidado sobre gerenciamento de dependências, o que diabos são dependências, e linguagens como Groovy, Scala, Elixir, Go e um pouco de Ruby e Ruby on Rails também.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/N6vgZr1k03g"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Esta é a Parte 2 do tema de Back-end para Iniciantes. Desta vez vamos olhar com mais cuidado sobre gerenciamento de dependências, o que diabos são dependências, e linguagens como Groovy, Scala, Elixir, Go e um pouco de Ruby e Ruby on Rails também.</p>
<p>Provavelmente este vai ser o episódio mais difícil deste canal até agora, se você é iniciante, e você vai precisar ver e rever várias vezes, mas se você está levando a sério se tornar um programador, o que vou dizer hoje é de extrema importância pra você entender e estudar bastante.</p>
<p>Além de tudo este é o episódio mais longo deste canal até hoje. Nesta mini-série de &ldquo;Começando aos 40&rdquo; eu estou resumindo e dando o contexto geral do que você aprenderia (ou não) em meses de faculdade e trabalhando na prática. Aproveitem pra entender a idéia geral e puxar todos os assuntos para estudar em mais detalhes sozinho. Mandem suas dúvidas nos comentários também! E não deixem de compartilhar com seus amigos.</p>
<p>Akitando:</p>
<ul>
<li>Playlist de Programação (<a href="https://www.youtube.com/watch?v=WObC_2e0kZk&amp;list=PLdsnXVqbHDUcrE56CH8sXaPF3TTqoBP2z"target="_blank" rel="noopener">https://www.youtube.com/watch?v=WObC_2e0kZk&list=PLdsnXVqbHDUcrE56CH8sXaPF3TTqoBP2z</a>)</li>
<li>Playlist Começando aos 40 (<a href="https://www.youtube.com/watch?v=O76ZfAIEukE&amp;list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki"target="_blank" rel="noopener">https://www.youtube.com/watch?v=O76ZfAIEukE&list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki</a>)</li>
<li>Sua Linguagem NÃO é Especial - Parte 1 (<a href="https://www.youtube.com/watch?v=p9-WuJbVHHc"target="_blank" rel="noopener">https://www.youtube.com/watch?v=p9-WuJbVHHc</a>)</li>
<li>Sua Linguagem NÃO é Especial - Parte 2 (<a href="https://www.youtube.com/watch?v=XcTTajFENHI"target="_blank" rel="noopener">https://www.youtube.com/watch?v=XcTTajFENHI</a>)</li>
</ul>
<p>Links:</p>
<ul>
<li>So, What&rsquo;s wrong with SBT? (<a href="http://www.lihaoyi.com/post/SowhatswrongwithSBT.html"target="_blank" rel="noopener">http://www.lihaoyi.com/post/SowhatswrongwithSBT.html</a>)</li>
<li>Reia (<a href="http://reia-lang.org/"target="_blank" rel="noopener">http://reia-lang.org/</a>)</li>
<li>Go is not Good (<a href="https://github.com/ksimka/go-is-not-good"target="_blank" rel="noopener">https://github.com/ksimka/go-is-not-good</a>)</li>
<li>Why Go is a Poorly Designed Language (<a href="https://medium.com/@tucnak/why-go-is-a-poorly-designed-language-1cc04e5daf2"target="_blank" rel="noopener">https://medium.com/@tucnak/why-go-is-a-poorly-designed-language-1cc04e5daf2</a>)</li>
<li>Why Go is not Good (<a href="http://yager.io/programming/go.html"target="_blank" rel="noopener">http://yager.io/programming/go.html</a>)</li>
</ul>
<p>Me sigam também em outras redes sociais:</p>
<ul>
<li>Twitter (<a href="https://twitter.com/akitaonrails"target="_blank" rel="noopener">https://twitter.com/akitaonrails</a>)</li>
<li>Instagram (<a href="https://instagram.com/akitaonrails"target="_blank" rel="noopener">https://instagram.com/akitaonrails</a>)</li>
<li>Facebook (<a href="https://facebook.com/akitaonrails"target="_blank" rel="noopener">https://facebook.com/akitaonrails</a>)</li>
<li>Quora (<a href="https://www.quora.com/profile/Fabio-Akita"target="_blank" rel="noopener">https://www.quora.com/profile/Fabio-Akita</a>)</li>
<li>Blog (<a href="https://www.akitaonrails.com"target="_blank" rel="noopener">https://www.akitaonrails.com</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Este já é sexto episódio da série Começando aos 40. O ideal é que vocês assistam todos os outros vídeos da série, e se possível os da playlist de programação que tem a rápida história das linguagens nos vídeos de Sua Linguagem Não é Especial. Eu vou deixar linkado nas descrições abaixo. Continuo bem contente com a repercussão dos últimos vídeos, parece que vocês estão gostando do formato então vou continuar assim até o fim desta série. Hoje por acaso estou sentindo minha garganta dando pra trás, é possível que minha voz comece a falhar até o fim do vídeo, mas vamos lá.</p>
<p>No episódio da semana passada eu expliquei os conceitos mais básicos da computação, sobre instruções de máquina, compilação, interpretadores, como os binários se linkam com suas dependências, lembra? Linkagem estática e dinâmica? Começamos a falar um pouco sobre Java e .NET e máquinas virtuais como JVM. Agora com os conceitos entendidos vamos entender porque eu disse que era importante entender tudo isso antes do que vou falar hoje.</p>
<p>Uma das coisas mais importantes pra um programador iniciante entender é que você nunca vai fazer softwares de verdade totalmente do zero, ou seja, que 100% do código foi só você que escreveu. Você vai ser obrigado a usar bibliotecas dos outros. Vamos expandir o conceito de bibliotecas dinâmicas e entender quão fundo vai o buraco desse coelho.</p>
<p>(&hellip;)</p>
<p>Até esse ponto na história você tinha então linguagens estilo Java, que é muito inspirado em C++ e alguns pontos de Smalltalk com alguns, mas não todos os conceitos de orientação a objetos e o conceito de máquina virtual e gerenciador de memória que também havia em Lisp. Na prática, em vez de orientação a objetos eu argumentaria que Java é uma linguagem mais orientada a classes. Muita coisa em Java não são objetos, começando com a própria classe em si. A orientação a objetos veio evoluindo desde Simula, CLU, C com Classes que originou C++, Smalltalk que foi provavelmente a melhor representação de orientação a objetos, e Java que deu alguns passos pra trás.</p>
<p>Até esse ponto já entendemos o conceito de bibliotecas dinâmicas que podem ser reusada. Bibliotecas em geral são conjuntos de funções que fazem alguma coisa útil como processar imagens, converter datas, formatar PDFs ou qualquer coisa assim que possa ser reusada. Componentes são funcionalidades, como um componente de calendário que tem a parte visual e a programação que controla as datas. Uma biblioteca pode ter vários componentes, por exemplo. A parte visual começou a ficar importante com o advento das interfaces gráficas, com a crescente adoção de ambientes gráficos como o Macintosh original, o OS/2 que foi um filho bastardo da IBM com a Microsoft e finalmente o Windows 3.1. Por acaso modelar componentes como classes e objetos era interessante e foi quando esses dois conceitos começaram a se mesclar.</p>
<p>No começo dos anos 90 um dos conceitos ligados a orientação a objetos começou a aparecer mais que foi a componentização. Em particular na forma de RAD ou Rapid Application Development, Na categoria de RAD foi onde se popularizou o conceito de Integrated Development Environment ou IDEs como os antigos PowerBuilder, Visual Basic, Visual dBase, Access, Delphi. As linguagens por baixo ainda não eram realmente orientadas a objeto como conhecemos mas aí surgiu o Java que trouxe uma evolução. Ainda longe do que era o Smalltalk mas já melhor do que Visual Basic.</p>
<p>Componentização também se tornou uma idéia importante primeiro para evitar reescrever software do zero toda vez e tornar mais fácil reusar software. Mas mais do que isso pra tornar mais fácil comercializar esses componentes. Hoje em dia se você quer uma biblioteca pra, sei lá, imprimir um PDF tem centenas disponíveis de graça no GitHub pra quase todas as linguagens. Mas naquela época a Internet só tava começando, nem 2% da população tinha acesso ainda. E o jeito pra se comercializar era distribuindo em disquetes, encaixotado, em prateleira de lojas. Pra começar, agora que tínhamos um ambiente gráfico mais sofisticado nos sistemas operacionais não dava pra todo programador toda hora ter que fazer tudo do zero. Desenhar um botão fazendo as linhas da borda pixel a pixel. Por isso linguagens como o Microsoft C++ já vem com um framework, o MFC ou Microsoft Foundation Classes e o Delphi vem como VCL ou Visual Components Library. Normalmente são conjuntos de DLLs como já expliquei antes.</p>
<p>Com C++ e MFC podemos criar aplicativos como um Word ou Excel. Mas daí a Microsoft foi além e criou outra tecnologia, o OLE ou Object Linking and Embedding que em resumo é uma forma de incluir componentes visuais complexos dinamicamente num aplicativo. Vocês já sabem o que são bibliotecas dinâmicas, mas do jeito como explicamos você precisa compilar o programa já com os bindings pras bibliotecas em tempo de compilação. Mas como fazer pra instalar um componente num programa que já existe sem recompilar? Uma demonstração impressionante na época era conseguir fazer uma tabela do Excel ser embutido dentro de um documento do Word e ser editável. Em cima do OLE surgiu o OLE control extensions ou OCX. Se você já instalou componentes num Access ou Delphi deve ter visto bibliotecas com extensão OCX que se tornou a principal forma de distribuir componentes em Windows.</p>
<p>Isso foi uma pequena revolução porque também permitia comercializar componentes num formato padrão que podia ser escrito em qualquer linguagem da época e embutida em qualquer aplicativo que suportasse o padrão, incluindo IDEs. Do OLE original pra OCX também se precisou de um modelo para linguagens interagirem com esses componentes. Então nasce o Component Object Model ou COM. É assim que componentização funcionou na época do Visual Basic e Delphi. No mundo Java o equivalente ao COM poderia ser um Java Bean. Hoje OCX é outra coisa, são componentes ActiveX. Assim como no Java toda classe herda de Object ou em Object Pascal toda classe herda de TObject, em COM você implementa a interface IUnknown.</p>
<p>Aqui vale entender a função de uma interface. Numa interface gráfica, tanto faz se você tem uma janela com um botão de OK em Windows, ou em MacOS, ou em GNOME do Linux, a interface é a mesma do ponto de vista do usuário, mas a programação, ou implementação por trás é completamente diferente. A interface garante que quem vai consumir o serviço por trás não precisa se preocupar como ele é construído. A mesma coisa são as interfaces para bibliotecas, nesse caso em vez de ser um elemento visual, se trata de um arquivo em um determinado formato que simplesmente declara que funções, com que argumentos, com que tipos de dados, e que tipo de resultados você espera. Daí você implementa como quiser por trás e quem consome essa biblioteca pode assumir que no mínimo ele vai implementar as funções conforme descritas na interface. Uma classe numa linguagem orientada a objetos é a implementação de alguma interface, como a tal IUnknown do padrão COM que falei antes.</p>
<p>Poderíamos argumentar que o modelo de componentes mais avançado na época, no começo dos anos 90, era a NextStep com sua linguagem Objective C que eu argumentaria que é mais simples, mais elegante e mais orientado a objetos do que C++ ou Object Pascal ou mesmo Java e tinha um conjunto de classes, que eles chamavam de kits como o Foundation Kit, melhor do que o MFC da Microsoft ou VCL da Borland. Pra vocês terem uma idéia de quão à frente do seu tempo ela estava, o desenvolvimento de iOS e MacOS de hoje ainda usa uma evolução direta das mesmas classes até hoje, por isso os nomes das classes como “NSString” ou “NSObject” começam com esse NS que é de NextStep. No fim dos anos 80 e começo dos anos 90, essa idéia de arrastar e soltar componentes direto numa janela gráfica pra criar programas era uma idéia revolucionária. No NextStep que surgiu o lendário primeiro navegador web de Tim Berners Lee, mas não só isso, pra nerds como eu é a Id Software desenvolveu a engine gráfica do jogo Doom. O também lendário John Carmack gostava dos NextCube.</p>
<p>A primeira geração de programas nessa época no fim dos anos 80 eram aplicativos desktop normais, como os feitos em PowerBuilder ou Access que rodavam só localmente em uma máquina. Esses programas normalmente tinham um binário da aplicação e outro arquivo que representava um banco de dados, muitos derivados do primitivo formato DBF (que literalmente significa database file) que usávamos em dBase ou Clipper ou FoxPro. O que a gente chamava de banco de dados era uma estrutura de dados muito simples, pense uma coisa parecida com uma lista ligada de estruturas e um índice primitivo. Numa época que empresas pequenas às vezes tinha só 1 computador pra empresa inteira, isso até funcionava. O banco inteiro era um único arquivo. E se dois usuários em duas máquinas quisessem compartilhar o mesmo banco de dados sem precisar ficar copiando o arquivo toda hora?</p>
<p>Com a popularização dos desktops, principalmente PCs e também com acesso a tecnologias de redes locais, antes ainda da Internet comercial realmente decolar, começa a surgir o conceito de aplicativos distribuídos. Foi com a popularização de tecnologias de redes como da Novell e do protocolo IPX/SPX e o Windows NetBios que permitiam compartilhar arquivos numa rede local que as coisas começaram a mudar. Com essa possibilidade, e se colocássemos o tal arquivo de banco de dados numa pasta compartilhada na rede, daí copiamos o programa na máquina de cada pessoa na rede e quando o programa abre ele pode apontar pro arquivo compartilhado e booom agora todo mundo pode acessar os mesmos dados! Sensacional né? Só que não &hellip;    Puta idéia errada.</p>
<p>Lembram no episódio passado que falei da dor de cabeça que é programar multi-threads porque cada thread tem acesso à memória global e uma pode ficar pisando na memória da outra? Mesma coisa com programas acessando um arquivo compartilhado na rede. ?O que acontece quando um programa tenta escrever no mesmo registro, no mesmo arquivo, que outro programa na rede? Caos!   Foi a era dos arquivos de banco de dados que corrompiam toda hora.</p>
<p>Essas linguagens primárias e as redes da época só tinham uma coisa pra fazer nessa época: bloquear o arquivo inteiro! Ou seja, se alguém decidisse gravar alguma coisa no banco de dados, todos os outros programas precisavam esperar a vez. Não era ideal mas em redes pequenas de 10 ou 20 máquinas não chegava a ser o fim do mundo. Mas o problema é que um arquivo compartilhado não é mágica. Tem um servidor onde fica o arquivo de verdade, na rede as pessoas não tem a cópia inteira do arquivo, ele vai lendo pedaços do arquivo em um stream de dados.</p>
<p>Aliás, esse conceito de stream é importante você entender se ainda não estudou. Se você tem um arquivo de, digamos, 100 GB na rede, e você quer só um pedaço no meio dele, você não precisa baixar o arquivo todo. Você abre um stream, tipo uma corrente ou esteira mesmo e vai puxando um pedaço de cada vez até chegar na parte que quer e aí pode fechar esse stream. Ou você pode abrir o stream direto já no meio do arquivo e ir lendo de lá um pedaço de cada vez. Quem é bem iniciante se confunde com isso porque você consegue listar os arquivos na rede como se estivesse local na sua máquina, mas “ver” os arquivos e “ter” os arquivos são coisas diferentes.</p>
<p>Mas se você tentar ler esse arquivo gigante inteiro, dependendo da velocidade da rede, que é sempre lento, pode levar muitos minutos. E na hora de gravar o servidor de rede precisava não errar onde ia gravar no meio do arquivo. ?E se seu programa dá pau na rede no meio de uma gravação? Ou se a rede dá pau? Ou se o servidor engasga? De repente a gente começou a ter um monte de variáveis a mais pra se preocupar onde antes era simplesmente abrir o arquivo, gravar e fechar.</p>
<p>Durante os anos 90 fomos evoluindo esse conceito de 2 camadas ou cliente-servidor primitivo. O próximo passo foi usar um servidor de banco de dados em vez de só compartilhar um arquivo na rede. Foi assim que começamos a usar bancos como Ingres, Caché, InterBase, IBM DB/2, Oracle e só depois que a Microsoft adquire a Sybase no meio dos anos 90 que ele constrói o SQL Server sobre ele.</p>
<p>A diferença com arquivos compartilhados é que em vez do programa cliente tentar abrir um arquivo na rede ele se conecta antes a outro programa que está remoto, um servidor. Nesse caso um servidor de banco de dados. Isso evita quase totalmente que o arquivo de dados em si se corrompa tão fácil. Esse servidor vai servir como um mediador, os clientes pedem a ele pra fazer operações no arquivo e o mediador vai executando na ordem correta as coisas, e como só ele abre e escreve no arquivo, as chances dele corromper eram muito menores.</p>
<p>Lembra no episódio sobre a história do front-end como eu expliquei que um navegador web é um cliente TCP e o servidor web é um servidor TCP? É a mesma coisa aqui, só que antes de começarmos a usar o protocolo TCP antes usávamos protocolos de rede local como o IPX que falei antes, então o programa em Delphi ou FoxPro podia ser um cliente IPX e o banco de dados era um servidor IPX. Pouco tempo depois que a Internet começou a se popularizar e o Windows finalmente começou a vir com uma pilha TCP/IP que começamos a trocar tudo por TCP.</p>
<p>Uma pequena evolução nessa arquitetura foi com bancos como Oracle ou SQL Server que permitem escrever programas que rodam dentro do servidor de banco de dados, os famigerados Stored Procedures em linguagens como PL/SQL ou T/SQL. Assim começamos a separar parte da lógica que ficava exclusivamente nos programas cliente para o lado do servidor. Isso ajuda em muito a diminuir a complexidade dos programas cliente, diminui a necessidade de ficar atualizando os programas nos desktops toda vez que a lógica de negócios muda. Mas por outro lado causa mais lentidão no banco de dados porque agora além de ter que fazer as operações básicas de ler e escrever dados ele vai precisar processar e computar cálculos e outras lógicas em cima desses dados.</p>
<p>De novo, lembre a dificuldade que era atualizar o programa cliente nos desktops. Você precisava gerar um novo binário e mandar todo mundo desligar tudo das máquinas, ir de máquina em máquina com um disquete pra instalar ou mandar todo mundo baixar do servidor de arquivos na rede. Pior ainda se a empresa tinha filiais em lugares geograficamente separados onde nem todas as filiais se comunicavam com as outras. Lembre-se, internet ainda era uma coisa opcional. A gente tinha redes locais dentro do escritório, mas não necessariamente internet entre os diferentes escritórios. ?E se uma filial começa a rodar o programa mais novo mas outra filial esquece e fica na versão mais velha?</p>
<p>Agora, alguém parou pra pensar: o banco de dados é um servidor de rede. Os programas são clientes que se conectam nesse servidor. Só que o banco faz duas funções: gerencia os dados e roda lógica de negócio com essas stored procedures. Porque não mais um servidor no MEIO do caminho? Daí o cliente se conecta nesse que tem lógica de processamento de dados primeiro, que por sua vez vai se conectar em um segundo servidor, que seria o de banco de dados. Dessa forma conseguimos tanto separar a lógica de negócio e diminuir o tamanho dos programas nos desktops e também tiramos a carga do servidor de banco de dados. Esse é o nascimento da arquitetura que conhecemos como três camadas ou three tiers. Se você assistiu o episódio sobre SAP é aqui que as soluções corporativas começam a se encaixar.</p>
<p>Com a idéia de um servidor de aplicação passamos a pensar mais em interoperabilidade e protocolos de rede. Junte a idéia de componentização, orientação a objetos e a necessidade de componentizar agora um conceito mais abstrato: chamar lógica de negócios em outro servidor. Assim nascem especificações como o COM e Distributed COM ou DCOM que depois seria renomeado como COM+ da Microsoft ou concorrentes como o famigerado CORBA. Na prática eram todas formas de chamadas de procedimento remotos ou o que chamamos hoje de RFC ou RPC, que é remote procedure calls.</p>
<p>Eu já expliquei que pra compilar um programa que depende de uma biblioteca dinâmica precisamos de um arquivo de cabeçalho que declara que funções essa biblioteca tem exposta para ser consumida, a tal da Interface. É exatamente a mesma coisa agora só que em vez de uma biblioteca dinâmica é uma biblioteca remota, mais precisamente um cliente de algum serviço remoto. Não temos como saber o que um servidor de aplicações tem de funções expostas então precisamos de algum tipo de interface que declara quaisfunções, com que argumentos, e que resultados esse servidor de aplicações expõe pra gente, através de que protocolo. Pra isso servem coisas como COM+ ou CORBA que tinham o protocolo IIOP ou Inter-ORB onde ORB significa object request broker, para especificar esse protocolo de comunicação, o formato das mensagens trocadas.</p>
<p>Esse conceito coincide com a fase do Java onde começa a surgir o famigerado J2EE ou Java 2 Enterprise Edition que hoje é chamado só de JEE, sem o dois. Java 2 é engraçado porque tivemos o Java 1, daí ele foi pro Java 1.1 e depois 1.2 e aí nasceu J2EE mas depois as versões foram 1.3, depois 1.4 e acho que só na 1.5 que ele chamou de direto de versão 5. Hoje acho que estamos no Java 10 ou 11 certo? Enfim, o J2EE era uma especificação realmente interessante porque ela consolidava tudo que tínhamos inventado até então em tecnologias de desenvolvimento de aplicações em três camadas e agora também usando tecnologias de internet como o protocolo TCP e HTTP.</p>
<p>Em resumo J2EE especifica o também famigerado servlet que é uma forma de encapsular as operações de HTTP, ele especifica uma forma complexa de gerar aplicações usando um modelo chamado Model 2 que é basicamente o Model - View - Controller ou MVC que você tanto ouve falar hoje. E na parte do model, que costuma ser uma abstração pra uma ou mais tabelas no banco de dados, temos os também famigerados EJB ou Enterprise Java Beans, além dos JMS ou Java Message Service, que encapsulam maneiras de acessar banco de dados remotos e outros tipos de serviços remotos como outros servidores de aplicação. E tudo isso conectado via arquivos de configuração. E nessa época se tornou moda formatar arquivos de configuração no novíssimo formato XML. Em breve a Microsoft ainda vai inventar Web Services usando o protocolo SOAP que trafega os dados todos formatados em XML também. Na verdade começamos a usar XML em tudo que é lugar. E assim começa a era onde passamos a associar tudo que tem XML com muito enterprise e passamos a odiar XML.</p>
<p>Em paralelo a isso, muita nova literatura começou a ser escrita pra ajudar a organizar toda essaa bagunça em metodologias e vocabulários pra conseguir desenhar sistemas complexos orientados a objetos. É quando surgem nomes com Os Tres Amigos, Grady Booch, James Raumbaugh, Ivar Jacobson, cada um com suas idéias de metodologias que se juntaram numa empresa chamada Rational e juntam suas idéias no famigerado Unified Modeling Language ou UML para representar soluções baseadas nessa idéias de programar objetos e componentes. Aliás, UML que podia ser representado em arquivos em que formato? Sim, em XML. A Rational que viria a desenvolver diversas ferramentas como Rose, ClearCase, ClearQuest, e outros. Depois teríamos a famosa gangue dos quatro ou Gang of Four que são Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides que escreveram o agora clássico livro de Design Patterns. O que chamamos hoje de mundo enterprise começa a ganhar forma nessa fase do fim dos anos 90, e ele começa de verdade com J2EE e as metodologias monumentais de engenharia de software orientados a objetos.</p>
<p>Em uma década evoluímos muito rápido de pequenos programas que rodavam só em uma máquina, pra soluções cliente-servidor em 2 camadas e agora a arquitetura em 3 camadas e as sementes para aplicações realmente distribuídas com o advento da Internet. Em paralelo, como já contei antes, tivemos C# com a origem num híbrido de J++ e Object Pascal. Como C# é basicamente a Microsoft que controla e basicamente depende só do Windows, ele conseguiu evoluir bem mais rápido do que Java nos anos seguinte, adicionando muito mais funcionalidades à linguagem que a tornaram mais do que simplesmente uma cópia da sintaxe do Java, tanto em termo de novas sintaxes quanto mais bibliotecas e serviços. COM+ e ActiveX se tornam as formas principais de componentização tanto local quanto remota e CORBA vai começando a desaparecer.</p>
<p>No mundo mais próximo de Linux, Perl já era popular principalmente como linguagem para scriptar coisas como ferramentas para administração de sistemas Linux. Ele veio pouco a pouco sendo substituído por Python, que era uma linguagem mais limpa e mais moderna, que também herdou algumas funcionalidades de orientação a objetos. Ele teve a sorte de pegar o crescimento das distribuições Linux e acabou se tornando a linguagem padrão em muitas distribuições. PHP pegou a onda das aplicações Web e ganhou espaço sendo mais fácil que Perl como sintaxe de linguagem. PHP se uniu a outras tecnologias emergentes no mundo open source como rodar em sistemas Linux, usar o servidor web Apache, e o ainda novato banco de dados MySQL, e assim nasce a pilha de tecnologias ou stack chamado de LAMP.</p>
<p>Mas o interpretador do PHP era realmente um puxadinho, bem ruim, sendo um dos piores em gerenciamento de memória e constantes bugs de segurança, numa época onde ainda estávamos aprendendo sobre segurança agora que começamos a colocar as coisas expostas na internet pública. Fora isso o estilo de programação derivado da filosofia de Larry Wall, criador do Perl, de “quick and dirty” ou “rápido pra desenvolver mas código sujo como resultado” não era atraente pra adoção mais séria da indústria. Mas a bolha da internet acelerou a adoção de todos eles ao mesmo tempo.</p>
<p>Com o crash da bolha demorou alguns anos pras pessoas voltarem a se interessar em novas linguagens. Como eu disse antes, o Manifesto Ágil só foi escrito bem no ano desse crash em 2001. Então o mercado de linguagens que já existiam não tinham muito interesse em adoção em massa de práticas como as ensinadas por metodologias como Extreme Programming ou Cristal. As metodologias num mundo como Java ou C# era ditado pelos fabricantes de ferramentas como a Rational que foi comprada pela IBM, BEA, Oracle, Microsoft. Google estava crescendo rápido mas ainda não tinha tanta influência nesse mercado. Facebook ainda não existia. O mercado passou a associar tecnologias enterprise como “sérias” e tecnologias open source como “amadoras”.</p>
<p>Mas a gente sabia que tinha jeitos mais simples de desenvolver, principalmente se olhar as linguagens interpretadas, mas ainda não havia uma adoção significativa a não ser do PHP, que pra muitos engenheiros era uma linguagem amadora e pouco sofisticada. Foi só quando Ruby começou a ser adotada por muitos dos signatários do Manifesto Ágil como Ward Cunningham, Martin Fowler, Andrew Hunt, Brian Marick, Robert C. Martin, Dave Thomas que as pessoas começaram a mostrar algum interesse e coincidentemente tivemos o lançamento do framework Ruby on Rails em 2004 e finalmente Ruby, que havia sido criado 10 anos antes em 1995 começou a ganhar tração. Diferente de Java ou C# ou PHP, em vez de ter origem na herança de sintaxe do C++ ele procurou inspiração no Smalltalk e Lisp. Isso a tornou diferente o suficiente e com o suficiente de sofisticação para atrair a atenção desses veteranos de cabelo branco.</p>
<p>Já que era muito difícil parar a inércia de gigantes como Java, .NET e PHP, pareceu interessante começar uma comunidade do zero, com uma linguagem sofisticada mas obscura, mas já aplicando as práticas evangelizadas pelas metodologias que seguiam o Manifesto Ágil. Essa Era marca o início da profissionalização das tecnologias open source para desenvolvimento de aplicações, que poucos anos antes eram consideradas mais amadoras pelo mundo enterprise. Em vez de convencer o mundo enterprise, um novo mundo começou a crescer rápido para competir com elas: as tech startups.</p>
<p>O sucesso quase instantâneo do Ruby on Rails para criar os tipos de aplicações que pouco tempo antes só deveriam ser feitos em Java ou C# trouxe uma nova idéia pra indústria: que pra uma linguagem fazer sucesso ela não precisava ser um clone da sintaxe de C++ ou Java ou ter o suporte de grandes nomes como IBM ou Microsoft. Nesse ponto estamos entre 2003 e 2007. Eu diria que esse período de transição é importante porque nos anos 80 surgiu de fato o movimento open source e GNU. Nos anos 90 surgiu o primeiro grande projeto que tornou open source relevante: a kernel do Linux e as distribuições de Linux.</p>
<p>Durante a bolha das ponto coms tivemos sucessos comerciais como o VA Linux e a RedHat. De repente open source não era só um hobby e tinha modelos de negócio viáveis que finalmente botaram pressão nas empresas tradicionais. Mas a manutenção e criação de novos projetos não era ainda uma coisa simples, haviam sites como SourceForge, CodeHaus e outros, mas contribuir em open source ainda tinha uma barreira de entrada muito alta. Lembre-se que GitHub só se torna realmente famoso a partir de 2008.</p>
<p>Em 2003 mesmo outra iniciativa estava começando com o surgimento do Groovy, uma nova linguagem que compila em bytecode pra JVM. Ela tem muita inspiração na sintaxe de linguagens como Python e Ruby e é uma linguagem muito mais agradável de trabalhar do que o Java original. E com o sucesso do Ruby on Rails, tivemos o Groovy on Grails. Mas nesse momento eu acho que a comunidade Java e mesmo a open source ainda não estava preparada pra se adaptar a uma nova linguagem. Um dos problemas de ser uma comunidade gigante é que é muito difícil mudar sua direção, especialmente quando essa direção ainda era meio incerta e experimental. Mesmo assim o Groovy teve um bom nicho e ainda hoje você vai ver Groovy se usa ferramentas como o Gradle. Groovy ganhou fôlego quando entrou no GitHub e quando ganhou patrocínio da Pivotal.</p>
<p>Pra quem não conhece a PivotalLabs foi uma consultoria que, junto com outros nomes emergentes como ThoughtWorks, ajudou a tornar Agile comercial mais visível, tinha na sua base tecnologias Java e Ruby também. Ela cresceu rápido pós 2008 e foi adquirida pela VMWare e depois pela EMC e hoje é uma empresa pública que mudou da área de serviços de consultoria pra produtos corporativos, mais especificamente cloud corporativo. Por 2 anos ela patrocinou o Groovy.</p>
<p>Voltando um pouco na história, até os anos 80 popularizou-se linguagens que chamamos de imperativas como C ou Pascal ou Perl. Elas são simples em estrutura. Você escreve funções que recebem argumentos e devolvem alguma estrutura de dados. Você compõe seus programas com funções que chamam outras funções. Rapidamente você nota que um conjunto de funções que atua em cima de uma mesma estrutura podem ser agrupadas em módulos. Assim nasce a idéia de linguagens como MODULA, precursor do Pascal. Mas então alguém resolve criar um conceito, ou uma abstração que uma estrutura com um grupo de funções pode ser o que chamamos de classes. E podemos criar várias instâncias dessa classe, que chamamos de objetos. Assim começa a nascer programação orientada a objetos.</p>
<p>Daí temos linguagens como Simula, Smalltalk, C++, Objective-C, Java, C#, Python. Organização de objetos pode ser implementada em cima de linguagens imperativas, então C com classes pode virar C++ ou Objective-C. Perl pode ganhar o equivalente a classes usando bless. PHP ganhou classes. Basic ganhou classes em Visual Basic. Pascal ganhou classes rudimentares em Turbo Pascal e depois em Object Pascal que é o Delphi. Até Cobol tem classes hoje em dia.</p>
<p>Em paralelo a isso havia outra área de pesquisas que deriva da linhagem de linguagens funcionais como Lisp, Scheme, ML, que lida com a matemática dessa composição de funções ou lambdas e é definida em Lambda Calculus, que se iniciou com o famoso Alonzo Church, conterrâneo de Alan Turing. Turing aliás, que foi aluno de Church, então você pode ver como as idéias dos dois se misturam e o trabalho de ambos é a fundação do que chamamos de Ciência da Computação. No caso da vertente de Church vamos simplificar assim, uma função recebe um argumento tipo um número ou uma string ou outra coisa, devolve outro tipo de elemento como resultado e, principalmente, tem um nome, e só.</p>
<p>?Mas por que toda função tem que ter um nome? Por que não podemos ter funções sem nome, o que chamamos de funções anônimas, e por que funções não podem receber funções como argumento e devolver funções como resultado? Em C temos algo rudimentar onde podemos passar o que chamávamos de callbacks, ou ponteiros pra memória onde está outra função, mas como eu disse era uma forma rudimentar.</p>
<p>Provavelmente uma das coisas mais diferentes que Ruby trouxe da herança de Lisp foi o conceito de lambdas para o mundo web, mesmo que ainda não tão avançado como era em Lisp, que implementava as idéias de lambda calculus de Church. Podemos chamar de funções anônimas, ou um tipo mais avançado que chamamos de closures ou blocos. Groovy também trouxe lambdas pra Java dentre outras coisas. Mas esse tipo de composição abriu caminho pra coisas como metaprogramação e DSLs ou Domain Specific Languages, que é como se fossem mini-linguagens em cima da linguagem principal. Em vez de somente seguir o que a linguagem dita e se pudéssemos manipular a própria estrutura e sintaxe da linguagem? A capacidade de moldar a linguagem pra ficar com a “cara” que você quisesse. Isso tornou o chato estilo imperativo de programar, em algo bem mais maleável e flexível e divertido, embora isso tenha aumentado mais a complexidade para quem está iniciando. Lambdas vieram pra ficar e depois disso toda nova linguagem e mesmo as antigas adotaram esses recursos. Hoje Java, C# e outros também ganharam lambdas e alguma capacidade de metaprogramação.</p>
<p>Com a virada do século e a aceleração antes do crash que tivemos nesse período, o poder computacional cresceu exponencialmente desde os anos 80, era a consequência da Lei de Moore, que ditava que o poder computacional dobraria mantendo o mesmo preço a cada 12 a 18 meses. Antes nos preocupávamos bem mais com a performance crua dos nossos programas pra tirar o máximo de máquinas caras e fracas, mas agora as máquinas finalmente ficaram poderosas e acessíveis, então passamos a ter surplus ou sobra de processamento. É importante salientar essa mudança de perspectiva. Finalmente em vez de termos que sofrer com linguagens simplistas e burocráticas em nome de economizar performance, porque não passar a pensar em formas de tornar a programação mais agradável?</p>
<p>Antes, linguagens mais simples de trabalhar era quase sinônimo de baixa performance, mas agora que a web provou que performance pura não era o único fator pra escolher uma linguagem, poderíamos começar a experimentar em funcionalidades e a escola de lambda calculus começou a aparecer novamente, graças aos programadores de cabelos brancos que haviam experimentado Lisp ou Scheme nas décadas de 70 a 80. E a tal orientação a objetos também veria uma evolução graças a quem experimentou Smalltalk no mesmo período.</p>
<p>Vale lembrar que Lisp já tinha o conceito de máquina virtual e garbage collector décadas antes de Java. E Smalltalk tinha o conceito de que tudo é realmente objeto e que objetos ficam vivos mesmo se a máquina virtual seja desligada. Pense num VirtualBox ou VMWare quando você sobre outro sistema operacional e então suspende a máquina. O Smalltalk tem esse conceito de suspender a execução sem bootar e limpar a memória, dentro do que ele chama de imagens ou no caso literalmente um banco de dados de objetos. Quando você abre uma imagem de Smalltalk ele pode ter objetos em memória que foram instanciados 30 anos atrás. E você vai compondo novos objetos usando os objetos vivos que já existem. Pensem que louco essa arquitetura.</p>
<p>E falando em arquiteturas exóticas. Uma editora de livros que ganhou notoriedade nessa virada de século foi a The Pragmatic Programmer, mesmo nome do clássico livro de 2 signatários do Manifesto Ágil e que ajudaram a dar credibilidade à adoção de Ruby: Dave Thomas e Andy Hunt. Eles também popularizaram o conceito de que todo programador deveria tentar aprender pelo menos uma nova linguagem por ano, e em 2007 eles publicaram suas experiências com a linguagem Erlang dos anos 80, de Joe Armstrong, de quando trabalhava na Ericsson. Erlang que como eu expliquei em outro episódio tem uma sintaxe muito diferente, herdada de Prolog, e que tinha uma arquitetura que ninguém no mundo web tinha visto ainda: o modelo de Atores. Erlang, como Java, tem uma máquina virtual. Mas diferente da JVM ele vai um passo além: ele tem partes do conceito de lambda calculus sem ser puramente funcional e partes das principais propriedades de objetos sem ser orientada a objetos. Mas em vez de organizar as coisas como classes e objetos, ele organiza como módulos e atores.</p>
<p>Um ator é como se fosse um objeto com seu estado encapsulado e se comunicando com outros atores via passagem de mensagem, diferente do Java, C++ ou C# e mais similar a Smalltalk e Objetive-C. E mais do que isso, num sistema operacional, além de carregar o Kernel ele também carrega serviços ou daemons via um serviço de supervisão como systemd ou initd ou outros dependendo do seu sabor favorito de Unix ou Linux. O Erlang tem algo similar, um sistema de supervisores que iniciam atores e se encarregam do ciclo de vida dos mesmos. Erlang assim como a JVM ou Smalltalk são quase como sistemas operacionais inteiros. Ao contrário dos interpretadores como Perl ou Python ou Ruby que foram feitos pra iniciar rápido e terminar rápido, Erlang e Java funcionam melhor se você iniciar e deixar elas parasitar a máquina toda.</p>
<p>Assim como Java a idéia é subir um único processo de Erlang e deixar que ele gerencie a máquina e seus programas rodando dentro dele. Erlang não foi feito pra ser reinicializado toda hora, e sim pra ficar de pé eternamente, inclusive essa era a grande funcionalidade pra indústria de telecomunicações: que você não estivesse numa ligação e de repente fosse cortado da sua conversa porque alguém precisou subir uma atualização. Ele tem inclusive o conceito de carregar código novo sem precisar necessariamente reinicializar tudo. Na prática é mais ou menos o que Java e J2EE também preferem: ficar de pé sem ficar reinicializando o tempo todo. Ficar de pé eternamente era o grande objetivo. Guardem essa característica porque vai ficar mais óbvio quando falarmos de infraestrutura e Devops mais pra frente. Mas o ponto é que essa linguagem super estranha despertou o interesse em arquiteturas muito diferentes do que estávamos usando.</p>
<p>Em 2009 Martin Odersky trouxe ao mundo o resultado das pesquisas que ele vinha fazendo no mundo de lambda calculus e Java desde os anos 80 e 90 na forma de outra linguagem que compilava em bytecodes Java como o Groovy. Essa linguagem foi Scala. E ele se popularizou muito rápido porque adicionava funcionalidades que tornavam o Java mais palatável, coisas como Traits que em Objective-C são o equivalente a Protocolos e alguns diriam que é uma forma mais limpa de se fazer herança de funcionalidades. Inferência de Tipos que eu já expliquei que vem de Hindley-Milner Type System de linguagens como ML, que levantou outro dilema da programação: a discussão se linguagens de tipagem estática são melhores ou não que tipagem dinâmicos.</p>
<p>Em um super resumo, todos os dados no seu código, variáveis, estruturas, listas, precisam ter tipos. Pense em tipos como moldes de biscoito. Você precisa dizer de antemão qual o tamanho do molde você vai querer, pequeno, grande. No caso de números, vai ser um inteiro de 32 bits? Um inteiro de 64 bits? Um inteiro sem sinal, ou seja, sem números negativos? Isso é importante pra um compilador saber quanto de memória precisa alocar pra cada coisa. Se fizer isso errado vai faltar memória e dar erro ou vai usar memória de forma desnecessária, causando leaks ou vazamentos. Em linguagens dinâmicas como Lisp, Python, Ruby a gente não determina isso no código, o interpretador pode determinar o molde, ou o tipo certo, durante a execução do código, por isso chamamos isso de tipos dinâmicos. Eles têm tipos, só que não estão estaticamente declarados no código como fazíamos em Java ou C#.</p>
<p>Declarar tipos é super tedioso e você pode errar quando está programando também. Simplesmente não declarar e deixar o interpretador decidir só quando já está rodando é muito mais conveniente mas pode causar desperdícios e alguns erros porque ele pode assumir o tipo que você não esperava e você só vai descobrir quando estiver rodando. Inferência de tipos é meio que o meio do caminho dos dois mundos porque a gente não escreve todos os tipos estaticamente no código, ou seja, deixa de ser absurdamente tedioso. Mas o compilador vai tentar adivinhar os tipos em tempo de compilação, daí o binário ou bytecode no final vai conter os tipos certos antes do programa executar. Só isso já torna escrever em Scala muito mais conveniente do que escrever em Java antigo. E Inferência de tipos foi melhorando e hoje se encontra em linguagens novas como Rust, Swift, Kotlin, Typescript.</p>
<p>Mesmo Java, desde a versão 5 você tinha possibilidade de omitir alguns tipos como quando usava a implementação rudimentar de Generics. No Java 8 você pode omitir tipos em declaração de lambdas. E no Java 10 ele ganha inferência de tipos locais como C# também. Como iniciante você vai notar que estudar tipos é muito importante, independente se você vai aprender uma linguagem dinâmica ou estática, se tem inferência de tipos ou não porque mesmo inferência não é mágica, o compilador ainda precisa de algum contexto ou anotação pra inferir o tipo certo. Na prática entenda: escolha o molde errado e seu biscoito vai sair do tamanho errado.</p>
<p>Scala cresceu rápido por um momento mas estagnou. Um grande problema do Scala é que o binário bytecode que ele gera de uma versão de Scala pra outra são incompatíveis. Ou seja, se você tem uma biblioteca compilada pra bytecode de Scala 1 e você faz um programa com Scala 2 e tenta carregar a biblioteca antiga, vai tomar um LinkageError da JVM. Isso era uma puta dor de cabeça: incompatibilidade binária entre coisas compiladas em diferentes versões de Scala. A única forma de solucionar isso é você recompilar tudo de novo sem reusar os binários antigos. É uma escolha de design do criador pra que ele pudesse inovar a linguagem sem se preocupar em ficar dando suporte pra coisas antigas. Hoje em dia o Go meio que exige a mesma coisa: ele prefere que você baixe o código fonte de todas as dependências, em vez de baixar binários pré-compilados, e compile tudo junto de uma só vez.</p>
<p>Aliás, um comentário importante: eu falei lá atrás que você como programador vai precisar sempre se preocupar com as dependências e as versões certas dessas dependências. Lembra da discussão de compilação estática vs compilação dinâmica? Nos anos 80 e 90 fabricantes de ferramentas de desenvolvimento como Borland, Microsoft, BEA, Oracle, traziam tudo junto. Eles que se viraram pra ter todas as dependências que você iria precisar já instalado ou de forma organizada que você pudesse comprar um CD e instalar na sua máquina. E mesmo assim os anos 90 foram infestados do que chamamos de “DLL Hell” com programas diferentes que precisam da mesma DLL mas em versões diferentes mas tudo no mesmo sistema entrando em conflito, literalmente um inferno de DLLs.</p>
<p>Com o advento do mundo open source, de repente você podia baixar bibliotecas gratuitas de código aberto e instalar você mesmo. Mas eles não estavam esperando por isso e virou uma grande bagunça no começo. Quem daquela época nunca copiou um site em PHP que na sua máquina tudo funciona mas quando copia pro servidor ele falha porque esqueceu de também copiar ou instalar as bibliotecas de dependência? Ou no caso de Java esquecia de copiar os tais Jars e a aplicação não queria nem iniciar?</p>
<p>Assim que nascem ferramentas como Maven no Java que é um grande banco de dados de bibliotecas binárias. Agora você tem um repositório centralizado onde quem escrevia bibliotecas poderia postar lá e você como desenvolvedor podia declarar num arquivo em XML, lógico, os nomes e versões das bibliotecas que precisava no seu projeto e o Maven se virava pra baixar tudo que você precisava. E como uma biblioteca pode precisar de outras bibliotecas, a gente costumava brincar que toda vez que precisávamos baixar todas as dependências do projeto, o Maven baixava a internet inteira … duas vezes. Hoje essa posição é do NPM do Javascript. No mundo Microsoft demorou mais pra algo similar aparecer mas no .NET você tem hoje NuGet que inclusive passou a vir pré-instalado no Visual Studio 2012 em diante.</p>
<p>No mundo de programação até esse ponto a gente tinha o Maven no mundo Java, e hoje muitos usam junto o Gradle que é feito em Groovy como eu disse antes. Temos o CPAN no mundo Perl até hoje. No mundo PHP era um híbrido, todo mundo baixava as dependências na mão mesmo tendo coisas como o Pear. Hoje em dia felizmente PHP tem algo mais moderno que é o PHP Composer. No mundo Ruby estávamos evoluindo a idéia de gerenciamento de versões com algo rudimentar implementado dentro do próprio Ruby on Rails, que baixava as dependências direto do GitHub e depois evoluímos pra ferramenta Bundler que na época se tornou o benchmark sobre o qual todo mundo se comparava.</p>
<p>Python preferia usar o próprio sistema de pacotes das distribuições Linux, então era uma zona porque você precisava fazer pacotes RPM ou DEB ou outros pra cada distribuição e você ainda tinha a zona de ferramentas como setuptools e easy_install, que de easy não tinha nada. Aliás, pra uma linguagem que se orgulha de ter um único jeito pra fazer cada coisa, instalar pacotes não era assim. Felizmente o mundo Python tem o PIP agora. No mundo Javascript como já falei nasceu o NPM que ganhou concorrência com o Yarn, co-autorado pelo mesmo autor do Bundler de Ruby. No mundo Elixir existe o Hex. No mundo Rust existe o Cargo. O mundo Go tem o dep, mas aqui a coisa é um pouco mais controversa.</p>
<p>Declarar dependências no seu projeto, instalar de algum repositório e gerenciar os upgrades de versão é uma coisa extremamente importante. Você nunca pode simplesmente atualizar todas as bibliotecas pras versões mais novas, porque seu código vai quebrar. Por outro lado você não pode ficar pra sempre com versões velhas, principalmente se elas tiverem relatos de buracos de segurança. Então você vai precisar atualizar as versões e alterar seu código pra comportar as mudanças. Vai ter vezes que o autor da biblioteca simplesmente desiste de manter sua biblioteca, vai ter buraco de segurança, agora você precisa achar outra biblioteca pra substituir e aí vai precisar reescrever toda a parte do seu código que dependia daquela biblioteca. É uma parte considerável do trabalho de ser um programador e manter um projeto grande.</p>
<p>Isso tudo dito, eu estava falando da incompatibilidade binária do Scala. Assim como Python tem PIP, Ruby tem Bundler o Scala tem o SBT que assim como muitas ferramentas tem a função híbrida de ser um gerenciador de pacotes e também um executor de tarefas. O Scala jogou na mão dos desenvolvedores de bibliotecas gerenciar diferentes binários de cada versão e tem um guia pra tentar manter compatibilidade binária ou o que fazer quando isso não é possível. É meio uma zona porque toda vez que o Scala lança uma versão nova, todo mundo que faz bibliotecas precisa ficar recompilando uma nova versão. Só que é irreal achar que todo mundo vai fazer isso, então você como programador que depende dessas bibliotecas, ou tem que ficar meses esperando todo mundo alcançar a versão nova, ou você mesmo precisa lidar com recompilar manualmente e ver se nada quebra.</p>
<p>Eu mencionei Go porque o Go é diferente da idéia de Java ou Scala, ele não é uma máquina virtual, nem é um interpretador. Ele é mais próximo de C++, ele compila direto num binário nativo e é feito para fazer aplicações e não drivers ou coisas mais baixo nível como fazemos em C. E ele prefere compilação estática pra criar binários que acabam ficando gigantes em tamanho mas que você pode mover pra outro lugar e ele simplesmente funciona. O Go fez uma coisa que a comunidade Ruby on Rails fez em 2008 ou 2009. Ele se integra direto com repositórios Git, em particular o GitHub. Todo desenvolvedor de bibliotecas mantém o código fonte no Github. Você usa o recurso de dep que eu falei pra baixar todas as dependências do GitHub e vai vir todos os código-fonte.</p>
<p>Como o intuito do Go é compilação estática ele precisa de todos os fontes disponíveis pra gerar um binário único. Então a menos que a sintaxe da linguagem não mude de forma drástica de uma versão pra outra, você sempre consegue um binário que não quebra no final sem depender de linkagem dinâmica e LinkageError que o Scala sofre. O Elixir faz algo similar com o Hex, o Rust faz algo similar com o Cargo. A idéia é sempre baixar os código-fontes das dependências e deixar próximo do código do seu próprio projeto e compilar tudo junto depois que baixa em vez de baixar bibliotecas em formato binário como os Jars de Java.</p>
<p>A diferença é que em Java o compilador é notoriamente lento. Em vez de toda vez compilar binários a partir do código fonte, nós compilamos e empacotamos em zips que chamamos de JARs, é o que se publica num Maven por exemplo. Daí você ou manualmente ou via Maven ou SBT no caso do Scala, baixa os JARs pré-compilados. A mesma coisa é no mundo .NET, mas a Microsoft tende a ser melhor em manter compatibilidade binária das suas dependências e agora de gerenciar as diferentes versões de cada uma. Antigamente isso era importante porque não se tinha esse conceito de código aberto, as empresas vendiam bibliotecas e preferiam não abrir o código, na verdade eles faziam o máximo inclusive pra obfuscar os bytecodes gerados pra dificultar alguém fazer engenharia reversa e ver como que ele foi implementado.</p>
<p>Por isso eu salientei a diferença na virada do século da explosão do mundo de código aberto e serviços como GitHub e linguagens interpretadas como PHP, Python, Ruby que precisam ter os códigos disponíveis. Scala ainda acaba herdando essa parte do Java, mas linguagens como Go, Rust e Elixir investiram em fazer compiladores bem mais velozes e baixar dependências na forma de pacotes que embutem o código-fonte e compilar seu programa e as dependências tudo localmente em vez de depender de versões binárias pré-compiladas.</p>
<p>Voltando ao papo de arquiteturas, Groovy e Scala trouxeram ao mundo Java uma sintaxe bem mais moderna do que o antigo Java e isso ajudou a linguagem a evoluir muito da versão 6 até agora. Scala também trouxe a arquitetura de Atores para o mundo Java e somado à The Pragmatic Programmer evangelizando Erlang, trouxe também a possibilidade de ir direto na fonte, já que foi ela que introduziu o conceito de Atores com seu framework OTP ou Open Telecom Platform. Softwares que foram escritos pra Erlang antes também começaram a ganhar a luz do dia, em particular ejabberd e RabbitMQ numa época onde produtos de comunicação como Facebook Messenger ou Whatsapp começaram a aparecer. Erlang, sendo um produto desenhado pra indústria de telecom era um par perfeito pra produtos de comunicação em massa. Mas sejamos honestos, a linguagem do Erlang é horrível de usar nos dias de hoje, talvez ok pros anos 80, mas não pro século XXI.</p>
<p>A comunidade Ruby começou a olhar Erlang e rubistas como Tony Arcieri criou a linguagem Reia. Assim como a JVM do Java roda os bytecodes compilados de linguagens como Groovy e não só do Java puro, o Erlang também tem uma máquina virtual chamada BEAM com seus próprios bytecodes. O Reia foi o primeiro experimento de tentar modernizar a sintaxe do Erlang, nesse caso imitando quase diretamente a sintaxe do Ruby para compilar em bytecodes que rodam no BEAM. Em paralelo a ele surgir outra tentativa de fazer a mesma coisa, desta vez de outro rubista, o brasileiro José Valim com a linguagem Elixir.</p>
<p>O Elixir foi uma tentativa mais avançada de não imitar diretamente o Ruby mas de criar uma sintaxe moderna em cima das capacidades do BEAM do Erlang e também de criar um conjunto de bibliotecas base para possibilitar realmente criar aplicações de verdade. Levou quase 4 anos pra Elixir finalmente se tornar usável de verdade pra fazer aplicações de verdade em produção. Ele aproveita tudo que o Erlang já faz mas com uma sintaxe e um conjunto de bibliotecas que remetem a coisas como Ruby, aumentando muito a produtividade. Também por causa da influência do framework Ruby on Rails surgiu o framework Phoenix para desenvolvimento de aplicações Web.</p>
<p>Ruby, como vocês já devem ter percebido, é uma linguagem que eu pessoalmente gosto. Ela ajudou a influenciar outras linguagens como Groovy, depois da comunidade saiu CoffeeScript que ajudou a influenciar o atual Javascript ES6, e também influenciou a sintaxe do Elixir e do estilo da programação de suas bibliotecas. Em paralelo começamos a experimentar mais com outras linguagens uma vez que o paradigma funcional, baseado no lambda calculus que falei acima começou a ganhar terreno. Linguagens obscuras como Haskell e OCAML começaram a ganhar um pouco mais de terreno, inclusive OCAML influenciando a linguagem F# no mundo .NET. Tudo isso também ajudou a evoluir a própria linguagem Java, agora com mais de 10 anos de idade.</p>
<p>Para finalizar este episódio preciso falar da linguagem Go mais um pouco, que eu já vim citando durante este episódio e o anterior. Todos sabem que é a linguagem do Google, mas eu não gosto muito de Go porque pra mim ela representa um passo pra trás. Ela foi criada em 2009 e ignorou muitos avanços que outras linguagens já estavam fazendo. Por exemplo, a inferência de tipos que eu falei que o Scala popularizou e linguagens modernas como Swift, Rust, C# e mesmo Java começaram a usar é menos poderosa em Go. Os tipos do Go são mais pobres, ele não implementa Traits como Rust. Em uma linguagem criada em 2009 que se poderia escolher tudo do melhor, ela preferiu o caminho mais fácil de escolher permitir o ponteiro nulo que é causa de muitos bugs em muitas linguagens. O sistema de tipos é mais pobre, os Generics são mais pobres. Ela não permite estender e redefinir a linguagem, não suporta sobrescrever coisas como operadores. Ela é de fato uma versão melhor do que um C++, que cresceu tão monstruoso e complexo que é realmente difícil de usar hoje em dia. Mas ela não evoluiu a linhagem das linguagens. Como linguagem ela representa um passo pra trás.</p>
<p>Nenhuma linguagem é perfeita mas cada nova linguagem veio evoluindo em cima dos seus antecessores e o Go é basicamente mais um caso da síndrome do Google de reinventar a roda. Eu quase diria que o Go é no back-end o que o Javascript se tornou no front-end: nós usamos mais porque existem softwares importantes escritos em Go, como o ultra famoso Docker, e agora ela está se tornando grande demais pra falhar, em breve não vai mais ser uma escolha não usarmos. Vou deixar linkado nas descrições abaixo um link com diversos artigos explicando todos os problemas dessa linguagem, ela compete com Javascript em críticas. Porém, não se pode negar que ela de fato trouxe algumas coisas importantes que vou explicar também no próximo episódio.</p>
<p>No próximo episódio eu vou continuar essa história com Go, mais Elixir, um pouco mais de Rust, mais de Javascript, talvez eu fale de Python e principalmente vou tentar explicar mais da importância da Apple nos últimos 20 anos para o desenvolvimento de software e como ela cruza com um evento importante no mundo open source.</p>
<p>A história do Back-end não acabou, eu disse que ia ser longo. No episódio de hoje eu voltei à década de 90 e dei uma breve introdução da evolução das arquiteturas de desenvolvimento de software, retornei mais ao mundo Java e entendemos mais sobre a longa e inacabada novela chamada gerenciamento de dependências, uma praga que vai te perseguir por toda sua carreira de programação e finalmente começamos a ter um gosto das novas linguagens, começando com Groovy, Scala, Erlang, Elixir, e um pouco do Go.</p>
<p>Acho que finalmente passamos da metade deste tema nesta série, não deixem de mandar suas perguntas nos comentários abaixo, se curtiu o vídeo mande um joinha, assinem o canal e pra não perder a continuação dessa história clique no sininho e não deixe de compartilhar com seus amigos. Como semana que vem temos Carnaval é possível que eu tire o feriado pra descansar, vamos ver. Se não nos vermos até lá, bom Carnaval pra todo mundo!</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>backend</category><category>programacao</category><category>akitando</category></item><item><title>[Akitando] #40 - Entendendo Back-End para Iniciantes em Programação (Parte 1) | Série "Começando aos 40"</title><link>https://akitaonrails.github.io/2019/02/20/akitando-40-entendendo-back-end-para-iniciantes-em-programacao-parte-1-serie-comecando-aos-40/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/02/20/akitando-40-entendendo-back-end-para-iniciantes-em-programacao-parte-1-serie-comecando-aos-40/</guid><pubDate>Wed, 20 Feb 2019 17:00:00 -0300</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/Qjk-cSW-jk4"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Este é o 5o episódio da série &amp;ldquo;Começando aos 40&amp;rdquo;. Você deve assistir os episódios anteriores da série pra entender onde estamos e recomendo assistir os 2 vídeos da série &amp;ldquo;Sua Linguagem Não É Especial&amp;rdquo;. No episódio de hoje vou começar a introduzir os conceitos básicos para o que chamamos de &amp;ldquo;back-end&amp;rdquo;, que na prática é a própria introdução à programação.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/Qjk-cSW-jk4"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Este é o 5o episódio da série &ldquo;Começando aos 40&rdquo;. Você deve assistir os episódios anteriores da série pra entender onde estamos e recomendo assistir os 2 vídeos da série &ldquo;Sua Linguagem Não É Especial&rdquo;. No episódio de hoje vou começar a introduzir os conceitos básicos para o que chamamos de &ldquo;back-end&rdquo;, que na prática é a própria introdução à programação.</p>
<p>A intenção do vídeo não é serem aulas nem tutoriais, mas conectar os pontos de diversos assuntos que vocês vão encontrar de forma desconexa se sair pesquisando pelo Google. Se você fez faculdade, já deveriam saber tudo que vou dizer aqui, então veja o que faltou nos estudos e acelere!</p>
<p>Em resumo, vou falar rapidamente sobre os conceitos mais simples, linguagem Assembly, compiladores, interpretadores, máquinas virtuais, bytecode, processos, forks, threads, tudo que qualquer programador tem obrigação de saber na ponta da língua e em paralelo vou passar rapidamente de novo pelos anos 90.</p>
<p>Como o episódio ficou longo, eu dividi em 2 partes. Na de hoje vou só até o crash das ponto coms em 2001, e vamos continuar dali na Parte 2 da semana que vem.</p>
<p>Links:</p>
<ul>
<li>
<p>Playlist &ldquo;Começando aos 40&rdquo; (<a href="https://www.youtube.com/watch?v=O76ZfAIEukE&amp;list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki"target="_blank" rel="noopener">https://www.youtube.com/watch?v=O76ZfAIEukE&list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki</a>)</p>
</li>
<li>
<p>Sua Linguagem Não É Especial Parte 1 (<a href="https://www.youtube.com/watch?v=p9-WuJbVHHc"target="_blank" rel="noopener">https://www.youtube.com/watch?v=p9-WuJbVHHc</a>)</p>
</li>
<li>
<p>Sua Linguagem Não É Especial Parte 2 (<a href="https://www.youtube.com/watch?v=XcTTajFENHI"target="_blank" rel="noopener">https://www.youtube.com/watch?v=XcTTajFENHI</a>)</p>
</li>
<li>
<p>Conheça a história (brasileira) da informática (<a href="https://keepgeek.wordpress.com/2010/08/01/conheca-a-historia-brasileira-da-informatica/"target="_blank" rel="noopener">https://keepgeek.wordpress.com/2010/08/01/conheca-a-historia-brasileira-da-informatica/</a>)</p>
</li>
<li>
<p>Meet the Unitron Mac 512 – the World’s First Macintosh Clone (<a href="https://www.cultofmac.com/266710/meet-unitron-mac-512-worlds-first-macintosh-clone/"target="_blank" rel="noopener">https://www.cultofmac.com/266710/meet-unitron-mac-512-worlds-first-macintosh-clone/</a>)</p>
</li>
</ul>
<p>Canais Retro:</p>
<ul>
<li>The 8-bit Guy (<a href="https://www.youtube.com/channel/UC8uT9cgJorJPWu7ITLGo9Ww"target="_blank" rel="noopener">https://www.youtube.com/channel/UC8uT9cgJorJPWu7ITLGo9Ww</a>)</li>
<li>Modern Vintage Computer (<a href="https://www.youtube.com/channel/UCjFaPUcJU1vwk193mnW_w1w"target="_blank" rel="noopener">https://www.youtube.com/channel/UCjFaPUcJU1vwk193mnW_w1w</a>)</li>
<li>LGR (<a href="https://www.youtube.com/user/phreakindee"target="_blank" rel="noopener">https://www.youtube.com/user/phreakindee</a>)</li>
</ul>
<p>Me sigam também em outras redes sociais:</p>
<ul>
<li>Twitter (<a href="https://twitter.com/akitaonrails"target="_blank" rel="noopener">https://twitter.com/akitaonrails</a>)</li>
<li>Instagram (<a href="https://instagram.com/akitaonrails"target="_blank" rel="noopener">https://instagram.com/akitaonrails</a>)</li>
<li>Facebook (<a href="https://facebook.com/akitaonrails"target="_blank" rel="noopener">https://facebook.com/akitaonrails</a>)</li>
<li>Quora (<a href="https://www.quora.com/profile/Fabio-Akita"target="_blank" rel="noopener">https://www.quora.com/profile/Fabio-Akita</a>)</li>
<li>Blog (<a href="https://www.akitaonrails.com"target="_blank" rel="noopener">https://www.akitaonrails.com</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Espero que vocês tenham sobrevivido ao meu último episódio onde gastei um longo tempo tentando dar contexto sobre as tecnologias de Front-End. Também espero que tenham entendido a receita: na dúvida, encontrem o contexto de porque determinada tecnologia foi criada. Tecnologia são ferramentas, ferramentas foram criadas com um propósito. Por isso não existe uma única tecnologia que resolve todos os problemas. Já diria o sábio que pra quem acha que só existe martelo, todos os problemas são pregos.</p>
<p>E seguindo nessa mesma linha, no episódio de HOJE, vou tentar dar contexto para as tecnologias que chamamos de Back-end. Porém, back-end é muito, mas muito maior do que front-end e mesmo se eu fizer um episódio de uma hora, não vai dar pra explicar tudo que eu quero. Eu comecei a escrever o script deste episódio, aí cheguei na página 10, fui escrevendo e cheguei na página 20 e mesmo vocês tendo gostado do episódio anterior que deu quase 50 minutos se eu tentar encaixar tudo agora vai facilmente dar mais de 2 horas. E se eu fizer isso vou correr demais e talvez não explicar direito, então vou ser obrigado a cortar em vários episódios e mesmo assim vai ficar grande! Então vamos lá porque temos bastante coisa pra cobrir.</p>
<p>Pra começar, a pergunta mais comum quando se fala em programação back-end pra Web é qual linguagem aprender? Javascript? Java? C#? PHP? Python? Ruby? Elixir? Go? Scala? Clojure? Rust?  Nos episódios sobre Sua Linguagem não é especial que eu espero que tenham assistido, eu dei uma rápida introdução nos primeiros 40 anos de história das linguagens e como uma linguagem veio influenciando as outras. Assistam essa história antes e, mesmo que não tenha intenção de se tornar um desenvolvedor front-end, assista o episódio anterior antes pra ter o contexto necessário. Se não assistiu vai lá, eu espero ….</p>
<p>Pronto, já foi? Então vamos lá!</p>
<p>(&hellip;)</p>
<p>No Brasil infelizmente nós praticamente pulamos a história da computação até os anos 80 e só começamos mais ou menos nos anos 90. Isso porque antes dos anos 80 vivíamos numa ditadura, e perdemos o timing da evolução dos transistores e nunca tivemos um mercado decente de microprocessadores. Nosso mercado era fechado a importar coisas de fora. Maldita reserva de mercado, toda reserva de mercado é um puta erro. A gente precisava ir até o Paraguai pra conseguir comprar máquina decente. Havia também computadores nacionais como os da Itautec e Microsiga que faziam clones de IBM PC e mesmo de outros como os famosos Sinclairs, quem não lembra da série TK como o TK-90? Tinha a Hotbit e Gradiente fazendo MSX que eu gostava bastante. E tinha a Unitron que fazia clones de Apple II e chegou até mesmo a fazer um clone excepcional do Mac 512, que era quase igual e rolou até uma controvérsia direto com a Apple onde eles quase pediram sanções dos EUA contra exportações do Brasil por causa disso.</p>
<p>Essa história é interessante e explica porque por muitos anos Steve Jobs odiou o Brasil, vou deixar linkado na descrição abaixo sobre isso. E tivemos até sistemas operacionais nacionais como o Sisne que era um clone de MS-DOS, só que em português. Eu brincava de Basic com computadores como Apple II e MSX quando ia na casa de amigos, meu primeiro mesmo veio do paraguai no fim dos anos 80. Foi um PC XT de talvez 10 Mhz ou algo assim, poderosos 1 MB de RAM e 10 MB de HD, com monitor CGA verde, de incríveis 4 tons de verde. Fiz muitos programas em dBase e Clipper nessa máquina, fiz muitos trabalhos de escola com ele. E joguei muito também. Você não precisa da máquina mais potente pra aprender a programar, você precisa aprender a controlar a máquina que tem e tirar o absoluto máximo que ela pode dar. Ninguém nunca vai dirigir bem uma Ferrari se sequer tem coordenação pra trocar marcha num Gol.</p>
<p>Vocês vão notar que mesmo hoje em dia, em pleno 2019, é muito raro encontrar programadores de cabelo branco. Eu mesmo só comecei a ganhar os meus nos últimos poucos anos. E esse é um dos motivos: havia muito pouco programador antes dos anos 80. Mesmo durante os anos 80 era difícil ser programador. Então os que estão na ativa são ainda mais raros. Nos Estados Unidos você vê vários, incluindo famosos como os Uncle Bob da vida. Por isso temos muito pouca gente realmente com décadas de experiência. Se programador no Brasil só se tornou uma carreira realmente mainstream depois do advento da Web, antes era bem mais nicho. E por isso você vê muito pouca gente que pode dizer coisas como: trabalhei em projetos com Lisp, ou Smalltalk, ou PROLOG, sem ser do meio acadêmico.</p>
<p>Bom, antes de falarmos sobre as linguagens novas, precisamos começar do começo. Todo hardware, mais especificamente a CPU vem de fábrica entendendo um certo conjunto de instruções ou funções. Eles tem registradores que é como se fosse um tipo de memória. Você coloca valores nesses registradores e chama uma instrução que é mais ou menos a mesma coisa que chamar uma função passando argumentos, se você começou a aprender alguma linguagem. Daí a CPU executa alguma coisa e grava o resultado em outros registradores e você pode ler a resposta. A linguagem que usamos pra falar diretamente com a máquina é Assembly, e seu montador, o assembler, traduz o código escrito em texto com mnemônicos como JMP ou ADD ou MOV diretamente nas instruções binárias da máquina.</p>
<p>Além do hardware temos instruções específicas do sistema operacional, o que chamamos de syscalls ou chamadas de sistema. Então o tal binário nativo que falamos são instruções pra máquina e pro sistema operacional que é uma abstração da máquina.  Sempre que falamos em abstrações pense que é uma forma de pegar uma sequência de instruções que, por si só, podem ser difíceis de entender o que vai fazer e encapsular numa chamada só com um nome mais descritivo. É o que você chamaria de funções. Um sistema operacional tem várias funções que abstraem instruções mais simples que a máquina oferece, como ler arquivos, ou ou alocar espaço na memória pra um programa.</p>
<p>É extremamente chato e trabalhoso escrever Assembly diretamente, além de ser bem ineficiente hoje em dia, tanto porque precisamos escrever muito código pra fazer uma aplicação inteira e porque a performance não vai ser melhor e vou explicar porque a seguir. Mas pior, o código que escrevemos é totalmente dependente da CPU que estamos usando. Programar Assembly de Intel x86 ou PowerPC ou ARM exigem instruções diferentes. Por isso temos linguagens como C. Um “compiladores” traduz o código como escrito em C e realiza diversas otimizações antes de terminar de traduzir em instruções binárias. Mas mais importante, podemos usar o mesmo código C com diferentes compiladores pra gerar as instruções de um Intel x86 ou ARM sem mudar o código C.</p>
<p>Uma linguagem como C cria um programa binário nativo. Ou seja, ele depende de chamar diretamente instruções do sistema operacional e da máquina. Um compilador faz muito mais que só traduzir código um pra um pra instruções de máquina, ele vai tentar o possível pra melhorar seu código primeiro. Vai tirar instruções que não servem pra nada. Vai tentar executar coisas fora de ordem se o resultado for o mesmo. Vai trocar pedaços inteiros por instruções mais simples. Vai tentar prever o que é o próximo resultado e já pré-calcular e assim por diante. Ele literalmente faz mágica, portanto parta do princípio que seu código deve ser escrito primariamente pra que outro ser humano consiga entender e é trabalho do compilador melhorar pra ficar mais eficiente na máquina. Você eventualmente pode otimizar seu código pra ficar mais eficiente quando ficar complexo, mas se sua linguagem exige que você faça isso o tempo todo, sua linguagem é defeituosa.</p>
<p>No processo de transformar um código texto em um binário que executa na máquina você precisa primeiro de um compilador. Só que ninguém escreve um programa 100% do zero. Normalmente você nem fala diretamente com o hardware ou o sistema operacional. Existem dezenas de bibliotecas que você deve reusar e que vão facilitar seu trabalho. No C você vai precisar de coisas como LIBC ou no C++ você talvez vai usar coisas como Boost. São grandes bibliotecas reusáveis pra você não ter que ficar reinventando a roda toda hora. No Windows seriam as DLLs que você já deve ter visto, como win32. No Linux a extensão dessas bibliotecas binárias é ponto SO. Agora você precisa de um linker que vai ligar seu programa compilado com essas bibliotecas. E você tem duas opções, linkar estaticamente ou linkar dinamicamente.</p>
<p>Pra linkar estaticamente você precisa do binários antes dele virarem um SO ou DLL, ou seja do que chamamos de arquivo de objetos, normalmente com extensão ponto O que é transformado em ponto A ou ponto LIB em Windows. Se seu programa é um binário e a biblioteca é outro binário, ao linkar estaticamente você vai ter as duas coisas mescladas num único bináriozão. Porém se todo programa que você fizer tiver que mesclar o binário das bibliotecas, vai ter um binários gigantes. Por outro lado você pode linkar dinamicamente, pra isso você precisa do arquivo de cabeçalho da biblioteca que é um código que declara quais funções estão expostas no binário dessa biblioteca. Dessa forma todos os programas vão ter tamanho menor e vão reusar o mesmo binário da biblioteca que está instalado no seu sistema.</p>
<p>Porém ao copiar esse binário pra outra máquina você precisa garantir que a biblioteca dinâmica está lá também. É por isso que muitos programas no Windows antigamente mandavam você instalar antes o runtime do Visual Basic 6 ou hoje em dia o runtime do .NET ou outros componentes como DirectX pra games. E mais, você precisa instalar a versão certa de cada biblioteca ou vai ter problemas. Se você já está instalando e configurando seu Linux do zero na mão, já deve ter aprendido sobre variáveis de ambiente. Não basta só baixar as bibliotecas e copiar em algum diretório qualquer. Pra que seus executáveis encontrem as bibliotecas, se você instalar na mão, vai precisar mexer em variáveis de ambiente como LD_LIBRARY_PATH que indicam onde procurar por elas.</p>
<p>Essa discussão se linkar dinamicamente ou estaticamente é melhor é uma das coisas que programadores gostam de discutir. Ambas tem vantagens e desvantagens. Se você tem todo o código-fonte de tudo disponível o tempo todo e quer um binário que você possa copiar pra outra máquina e não se preocupar com dependências, pode compilar tudo estático, ficar com um binário gigante e quando eu digo gigante estou falando de binários de 200Mb ou mais ainda. É o que linguagens novas como o Go do Google fazem: ele prefere baixar o código fonte das dependências e compilar e linkar tudo estaticamente. Tem algumas exceções que vou falar depois mas na maior parte do tempo todo binário de Go você pode gerar na sua máquina, copiar num servidor e na maior parte do tempo tudo funciona sem se preocupar em precisar instalar dependências dinâmicas.</p>
<p>Mas se é tão simples assim e ficar se preocupando com versões de bibliotecas dinâmicas ou se elas estão instaladas é tão chato, porque não compilar tudo estaticamente? Primeiro porque você não precisa ter o código fonte das bibliotecas, só o arquivo de cabeçalho que lista suas funções. Segundo, porque digamos que você tenha 10 programas, todas elas linkam estaticamente uma biblioteca de criptografia. Digamos que descobrimos que tem um bug de segurança crítico nessa biblioteca. Se todos os programas estiverem linkados dinamicamente, basta recompilar só a biblioteca de criptografia e substituir na máquina em cima da antiga e todos os 10 programas automaticamente vão carregar a versão corrigida. Caso contrário, você agora precisa recompilar todos esses 10 programas pra que eles gerem novos binários com as correções e substituir na máquina um a um. Tudo sempre depende de pra que serve seu programa. Aliás, programação é muito isso: aprender a fazer escolhas entre opções que não estão erradas, que você precisa saber o contexto. Nem sempre as escolhas são simples, vá se acostumando com isso. É como em arte, qual cor é melhor? Vermelho? Azul? Amarelo? Não existe cor certa. Existe saber usar a cor certa no lugar certo.</p>
<p>Na maioria das distribuições Linux existe o conceito de um gerenciador de pacotes. No Redhat tempos o RPM e a ferramenta yum, em distribuições derivadas de Debian como o Ubuntu tempos DEB e a ferramenta apt.
Em Arch Linux e derivados como Manjaro temos o pacman. E assim por diante. Mas quando não existe o pacote de algum programa que queremos você normalmente baixa um tarball que é tipo um zip, e vai precisar descomprimir, compilar e instalar a partir desse código fonte. E você normalmente vai ver a seguinte sequência de 3 comandos que precisa executar na linha de comando. Primeiro tem o ./configure. Configure é responsável justamente por checar se você tem todas as dependências que se precisa pra compilar já instalados na sua máquina, se não tiver ele vai dar erro e você precisa instalar as dependências e rodar configure de novo. Daí você roda make. Make lê um arquivo chamado Makefile que vem no projeto e que declara tarefas que a ferramenta make pode executar, e o default ou padrão é compilar.
Ele vai chamar compiladores como o GCC ou CLANG e transformar o código C em binários.
E por fim você tem o make install que é o make chamando a tarefa install do Makefile que vai pegar esse binário que acabou de compilar, dar permissão de executar, e mover pra um diretório que o PATH consiga encontrar, como o /usr/local/bin ou pra outro lugar arbitrário tipo /opt/blabla e colocar esse diretório no PATH se precisar. Toda vez que você faz essa sequência vê como demora.</p>
<p>Programas modernos são dependentes de dezenas de bibliotecas e o compilador tem bastante trabalho pra transformar centenas de arquivos ponto C em ponto O e depois no binário final linkado estaticamente ou dinamicamente com as bibliotecas que precisa, e só aí finalmente você pode executar seu programa. Em Windows você quase nunca instala programas assim, você normalmente já baixa o binário que vem embutido num instalador. O instalador normalmente instala as dependências que precisa. Em Windows, ao contrário de Linux, preferimos manter todas as versões de uma determinada biblioteca no sistema. Por isso você tende a ter um sistema operacional mais gordo que o Linux e com bibliotecas bem antigas ainda disponíveis, mas a vantagem é que conseguimos pegar um binário feito pra Windows 98 e ele vai provavelmente funcionar no Windows 10. MacOS é um híbrido dos dois conceitos, ele mantém muitas bibliotecas antigas até um certo ponto, mas não tão longe quanto o Windows, e ao contrário do Windows ele trás já os compiladores e dá a opção de compilar direto do código-fonte mantendo os cabeçalhos de bibliotecas que precisam pra compilar. Por isso existem adaptações de programas de Linux que funcionam perfeitamente em MacOS.</p>
<p>Você vai ver mais sobre esse tipo de escolha quando estiver usando linguagens que compilam em binários nativos, como C, C++, Pascal ou Object Pascal do Delphi, Objective-C ou Swift pra iOS e até em linguagens novas como o Google Go que eu falei a pouco e o Rust da Mozilla. Mas do outro lado espectro existe o conceito de um interpretador. Um interpretador é em si só um programa mas a função dele é ler código que você escreveu e traduzir em instruções pra máquina sem necessariamente precisar compilar esse código em binário nativo. Ou seja, ele depende de ter o código fonte do seu programa e traduzir, ou interpretar, o código toda vez que carrega.</p>
<p>Ele contém partes de um compilador, como o parser. Pra interpretar o código que você escreveu, precisamos de um programa que leia esse código e interprete de acordo com uma gramática da linguagem e traduza isso em instruções de máquina ou uma representação similar, pra isso serve o parser. Aqui que a coisa começa a ficar confuso, mas vamos focar pra tentar entender. Originalmente um interpretador é só um programa como eu já disse, você executa na linha de comando e passa como argumento o caminho pra um arquivo de texto que contém o código que você escreveu na linguagem que esse interpretador entende. Ele vai carregar esse arquivo, vai usar o parser pra checar que você escreveu certo, vai transformar numa representação interna que ele entende, normalmente numa árvore - lembra que eu falei no episódio anterior da importância de você entender estruturas de árvores? E finalmente vai começar a interpretar e executar as instruções pra fazer o que você pediu. Quando terminar seu programa, o interpretador também termina.</p>
<p>É assim que funcionam originalmente linguagens como Perl, PHP, Python, Ruby, até Javascript. Elas variam em algumas funcionalidades, por exemplo, Perl e Python permitem pré-compilar seu código fonte na representação intermediária que eu falei. O termo “compilar” pode confundir porque você pode achar que ele está compilando num binário nativo como o compilador de C, mas é diferente. Ele apenas vai economizar o tempo de ler o arquivo texto e parsear na tal estrutura de árvore que num Ruby chamamos de AST ou Abstract Syntax Tree. Por isso em Perl você tem a extensão ponto PL pro código fonte e ponto PLX pro pré-compilado ou em Python você tem ponto PY pro código fonte e ponto PYC pro pré-compilado. Ruby e Javascript não tem essa funcionalidade ainda. Na prática tanto faz, hoje em dia os HDs são rápidos o suficiente pra não importar tanto isso.</p>
<p>Antigamente fazia sentido porque a principal função de um interpretador era permitir que você pudesse criar programas de linha de comando que executam e terminam muito rápido. Você podia chamar de duas formas: digitando perl e passando como argumento o arquivo do código ou ligando o flag de execução direto do arquivo texto de código e tendo como primeira linha o que chamamos de “shebang” que é a cerquilha com exclamação e o caminho absoluto pro executável do interpretador. E se você já fuçou pela Web por scripts deve ter visto isso. Existia ainda outra opção que era criar um pacote que embute o interpretador junto com a representação do seu código fonte num único executável. O Perl permitia isso e é o que o Visual Basic antes da versão 6 também fazia. No final parecia que era um binário nativo como de um C, mas na verdade era um pacote que embutia as duas coisas. De curiosidade eu disse Visual Basic até o 6 porque no 6 você tinha a opção de compilar o código fonte em binário nativo quando ele passou a adaptar e usar o compilador do Visual C++. Visual Basic 6 é um produto que me impressionou bastante na época.</p>
<p>Como eu disse antes, um interpretador originalmente foi feito pra iniciar rápido e terminar rápido. No episódio anterior eu comecei a falar sobre servidores web. Todo sistema operacional lida com processos. Toda vez que você executa uma aplicação, seja o executável nativo compilado em C ou os interpretadores que citei antes que também são feitos em C ou C++, o trabalho do sistema operacional é carregar, alocar memória e criar um processo em execução. Em ambientes UNIX ou principalmente Linux criar processos é uma coisa relativamente barata, ordens de grandeza se comparar com MacOS ou Windows. Em Windows criar processos envolve mais peso ou o que chamamos de overhead, não é uma coisa barata e nem é porque ele é pior mas porque tem requerimentos diferentes. Pensando antigamente num UNIX, um programador naturalmente  faria um programa que é executado e, se for um servidor TCP, ele só consegue servir um cliente TCP de cada vez, como já expliquei no episódio anterior.</p>
<p>Pra conseguir servir múltiplos clientes TCP ao mesmo tempo, a coisa mais fácil de se fazer é um FORK do processo, ou seja, criar uma cópia do programa em execução na memória. Cada nova conexão o servidor faz um novo FORK. E esse fork é razoavelmente rápido de criar e usa pouca memória porque ele reusa os mesmos bits do programa pai original que também está na memória, é um truque do sistema operacional e esse recurso é chamado de COW ou Copy on Write que evita fazer uma cópia inteira do executável da memória pra outro espaço de memória. Então se um processo precisa de 200 kbytes em memória, no instante que fizer fork ele não vai dobrar a memória e usar 400, vai usar muito menos que isso.</p>
<p>Um servidor web como o Apache antigo que eu falei, no começo só usava FORK. Por isso ele era simples. E mais do que isso, ganhou a capacidade de executar programas dependendo da URL que era pedido, através do padrão que falei que ficou conhecido como CGI. No começo esses executáveis eram só programas nativos compilados de C. Mas falei que rapidamente fizeram Perl funcionar no Apache também e começamos a era da web dinâmica. Pois bem, interpretadores com programas curtos iniciam e terminam rápido. Mas se o programa vai ficando mais e mais complicado, incluindo coisas custosas como conectar num banco de dados ou manipular arquivos, ou carregar muitos dados em memória, é muito custoso ficar iniciando e terminando esses programas toda vez. Foi quando surgiu a primeira gambiarra na forma de coisas como o mod_perl e depois o mod_php que são módulos compilados dentro do Apache e que dá inclusive acesso às estruturas internas do Apache. Toda vez que um novo fork do Apache é feito ele já pré-carrega com a capacidade de executar Perl ou PHP sem precisar carregar o interpretador separadamente. Isso é mais rápido do que CGI puro, porém é um terrível buraco de segurança já que bugs no Perl ou no PHP dão acesso ao Apache inteiro e a tudo que ele tem acesso na máquina.</p>
<p>A segunda gambiarra pra evitar ter que carregar os interpretadores a cada nova requisição foi o FastCGI que é basicamente um segundo servidor que carrega paralelo ao Apache ou outro servidor web, ele mantém o interpretador e seu programa ativos em memória e pode servir múltiplas requisições sem precisar terminar e reiniciar toda vez. Daí o servidor web e o servidor de FastCGI se comunicam via TCP ou outras formas como Unix Sockets que é recurso em Unix para que dois processos consigam se comunicar. Quando uma requisição chega ao Apache ele passa essa informação e outros metadados pro servidor de FastCGI executar o programa Perl ou PHP e devolve a resposta resultante de volta pro Apache devolver pro navegador. Uma grande vantagem disso era poder isolar os dois servidores de forma que um não invada demais o espaço do outro em caso de bugs de segurança.</p>
<p>Mas em Windows, você não tem o conceito de FORK na API de alto nível do sistema, o CreateProcess. Internamente o kernel do Windows permite algo parecido com FORK incluindo o recurso de COW ou Copy on Write, mas não é tão usado e mesmo assim ainda é uma ordem de grandeza mais lento. Isso explica porque os primeiros Apache eram uma porcaria em Windows, nem perto da performance num Linux e por isso dávamos preferência a usar o próprio IIS da Microsoft. Só porque dois programas compilam pra dois sistemas operacionais diferentes e rodam, não significa que rodam igual. Coisas feitas pra Windows funcionam melhor em Windows e coisas feitas pra Linux só rodam bem em Linux. Lembre-se disso. Dada essa limitação você acaba sendo obrigado a usar mais Threads que são linhas de execução em paralelo dentro de um mesmo processo.</p>
<p>Já falamos da discussão de compilação estática vs compilação dinâmica. É hora de falar de outro dilema da programação: programação baseada em processos com fork versus um processo com múltiplas threads. Hoje em dia usamos muito mais Threads mas processos com fork não estão obsoletos, por exemplo, o conector do banco de dados PostgreSQL funciona via fork assim como muitos outros softwares. Como eu disse antes, toda vez que você executa um programa o sistema operacional carrega os bits em memória e inicia uma entidade chamada processo. O sistema operacional tem o poder de matar esse processo ou pausar e reiniciar sua execução. Dentro do processo ele não tem consciência de todos os outros processos ao seu redor a menos que tenha privilégios de perguntar ao sistema operacional. Pra todos os efeitos, ele se sente como se fosse a única coisa rodando na máquina. Quando o processo precisa escrever na memória ele não escreve diretamente.</p>
<p>Pense na memória como um livro com páginas numeradas sequencialmente de 1 até um número grande. Num computador de 32-bits temos a capacidade de mapear de 1 até 2 elevado a 32 ou seja mais de 4 bilhões ou 4GB. Num computador de 64-bits temos a capacidade de mapear até 2 elevado a 64 ou seja mais de 18  quintilhões de bytes ou 18 exabytes, que é bastante coisa. Se tem vários programas na memória você imaginaria que o certo seria cada programa começar a partir de uma determinada página dessa memória, tipo o Word começando na página 1, o Excel na página 100, o Chrome na página 200 e assim por diante, mas isso seria extremamente complicado de controlar. Em vez disso todo mundo começa na página 1, mas de uma memória virtual e o sistema operacional que se encarrega de traduzir a página 1 da memória virtual do Excel pra página 100 da memória real. Então o Excel nunca teria como acessar a memória do Chrome. E assim começamos a criar um mínimo de segurança entre os programas. No MS-DOS antigo que rodava no que chamamos de modo real, cada programa tem acesso a todos os endereços reais da máquina, mas não tinha problema porque no MS-DOS só dava pra rodar um programa de cada vez. Pra trocar do Word pro Excel, tinha que primeiro sair do Word e daí carregar o Excel. Mas hoje em dia onde vários programas.</p>
<p>Isso facilita muito pra nós programadores porque não precisamos nos preocupar com a memória de terceiros. E se precisamos fazer algo como um servidor Web onde cada conexão de um cliente TCP precisa rodar em paralelo a outras conexões, podemos fazer só um FORK do processo pra cada um. Cada FORK é um processo isolado e daí um não afeta a memória do outro. Tudo funciona perfeitamente. Mas se é caro,  ou não é possível fazer FORKS, a única outra solução é dentro de um único processo iniciarmos múltiplas Threads, que são bem mais baratas que forks. Cada Thread daí pode servir um cliente TCP conectado ao processo. Mas agora começamos a ter problemas. Porque não existe memória isolada pra cada thread, dentro do processo cada thread tem acesso a toda a memória virtual do processo. Por isso você vai ouvir falar, caso ainda não tenha passado por isso, que escrever programas que são multi-thread é uma puta dor de cabeça, porque você precisa garantir o que chamamos de thread-safety ou seja, código seguro pra rodar em threads. Nesse caso, você como programador tem a responsabilidade de escrever código que, quando executado numa thread, não pise sem querer na memória de outra thread executando ao mesmo tempo. A forma de fazer isso normalmente envolve toda vez que você precisa escrever algum dado em memória, primeiro você notifica o sistema que vai escrever, pega o que chamamos de um lock, escreve e depois libera esse lock. Se falhar em pegar o lock, se arrisca a corromper a memória de outra thread. Se esquecer de soltar o lock vai causar um deadlock e bloquear outras threads de pegar o lock. Pense essa rotina em milhares de linhas de código. Ou seja, dor de cabeça.</p>
<p>Recapitulando, um programa binário é executado pelo sistema operacional num espaço de memória isolado, chamado processo. Cada processo pode ser clonado via fork num Linux. E cada processo pode rodar uma ou mais threads dentro dele. Um servidor web é um processo e pra servir múltiplas requisições ele faz um fork pra cada uma. Daí nasce CGI ou FastCGI. Mas em Windows como forks são ordens de grandeza mais caros, o servidor web IIS precisa de outro truque e isso veio na forma do padrão ISAPI que é como se fosse um programa escrito pra CGI mas com modificações pra ser thread-safe e compilado dentro de uma biblioteca DLL que é carregado pelo IIS. O servidor web da Netscape, que não existe mais, tinha outro padrão similar, o NSAPI. Muitos programadores de Delphi já devem ter feito programas pra Web e compilado em DLLs ISAPI ou NSAPI.</p>
<p>Com o advento de coisas como CGI, FastCGI, ISAPI, NSAPI e outras arquiteturas e o fato de termos interpretadores que possibilitam escrever código que tem pouca dificuldade de rodar porque não precisamos nos preocupar com compilação e gerenciamento de dependências, é quando linguagens interpretadas como Perl, PHP e ASP Clássico ganham notoriedade no mercado, especialmente com o timing do início da bolha das ponto coms a partir do meio dos anos 90.</p>
<p>Voltando a threads mais um pouco, como faz pra várias threads rodarem em paralelo? Pense assim, um processo tem pelo menos 1 thread. Pra um processo executar múltiplas threads ao mesmo tempo, o CPU tem uma coisa chamada agendador ou scheduler, mas só pode rodar um número limitado de threads ao mesmo tempo. Então ele pausa uma thread em execução de acordo com vários critérios e acorda outra thread que tava pausada pra dar chance dela rodar e vai fazendo isso com os vários threads de tal maneira que você tem a impressão que estão todos rodando ao mesmo tempo. E quando você pede o tal lock é esse scheduler que vai evitar que outras threads pisem no seu calo por exemplo. Programação multi-thread vai ser uma longa jornada pra vocês que estão iniciando e é até hoje considerada uma das coisas mais difíceis pra um programador realmente entender, só competindo com gerenciamento de memória e segurança.</p>
<p>Entendendo esses conceitos básicos que eu expliquei muito rapidamente, podemos começar a entender coisas como o surgimento do Java. Você precisaria ter tido experiência com C++, Smalltalk e Lisp pra entender realmente como um Java nasce mas vamos começar por aqui. Em 1991 surge uma nova linguagem na Sun com o codenome Oak, e o objetivo inicial era ser um sistema pra set-top-boxes. Naquela época se tinha o conceito que as pessoas teriam “caixas” multimidia nas salas integrando a tv a cabo e outros conteúdos. Esse conceito anos depois viria a se tornar um Tivo ou Roku ou mesmo Google Chromecast e Apple TV de hoje, que inclusive seriam substituídos pelas Smart TVs e consoles de videogame também. Lembrem-se que Linux ainda estava só no começo, ainda era rudimentar, e coisas como Android estão muitos anos à frente ainda.</p>
<p>Java embute alguns conceitos de compiladores e interpretadores. Mais especificamente de máquinas virtuais, por isso você tem a JVM ou Java Virtual Machine. Uma máquina virtual é uma evolução de um interpretador. Em vez de ser só um programa que executa código escrito numa determinada linguagem, como Java, a JVM tem uma ambição maior, ele quer ser o próprio sistema operacional e abstrair a máquina real por baixo. Pro programa em Java não existe um sistema operacional Linux ou Windows rodando num processador Intel ou ARM. Só existe a JVM.</p>
<p>O conjunto todo que a Sun inventou tem várias ferramentas. Diferente de um interpretador e mais similar a uma linguagem compilada como C++, ele separa a fase de compilação da fase de execução. O compilador JAVAC pega seu código fonte escrito em Java (eles usaram o mesmo nome pra tudo e isso dificulta explicar, eu sei) e compila numa representação intermediária que chamamos de Java Byte Code. Na prática, lembra que eu falei que o compilador de C traduz seu código C em instruções nativas do sistema operacional e do processador? E se seu sistema operacional e processador forem a JVM? Também falei que cada processador hardware vem com um conjunto particular de instruções que só roda nesse processador. No caso da JVM é a mesma coisa, é isso que chamamos de bytecode. Então quando o JAVAC compila, ele gera um binário nativo só que específico pra máquina virtual Java. Na época se pesquisou inclusive fazer um processador hardware mesmo que entendia essas instruções mas nunca foi mesmo pra frente. Entenderam? A JVM é como se simulasse um computador.</p>
<p>Assim como um interpretador, você carrega a JVM e aí ele carrega o seu programa, abstraindo o sistema real por baixo. Mas diferente de um interpretador ele faz coisas mais pesadas e tem premissas mais caras. Java é péssimo pra ser algo como substituto de Perl na linha de comando porque a inicialização dele é extremamente lenta até hoje. Isso porque ele tem um gerenciador de memória muito mais sofisticado do que a maioria dos interpretadores e carrega muito mais coisas na inicialização. Por causa disso também ele não é bom pra fazer FORK pra rodar em múltiplos processos, porque o custo desse gerenciador de memória é extremamente alto. Como simulador de um sistema operacional, nesse sentido ele se parece mais com o Windows do que um Linux. Então o caso de uso ideal do Java é rodar um único processo, sozinho na máquina, e seus programas rodam em paralelo dentro da JVM e a JVM substitui o sistema operacional pra gerenciar seus programas rodando em paralelo. E cada programa seu precisa usar Threads pra executar coisas em paralelo dentro dele. Viu porque eu disse que a JVM praticamente substitui o sistema operacional? Você tira o máximo dele quando não roda mais nada além da JVM e dá todos os recursos da máquina pra ele gerenciar.</p>
<p>E eu disse que compiladores como C fazem dezenas de otimizações pra reescrever seu código da maneira que rode mais rápido possível. A JVM vai um passo além, ele fica medindo como seu programa roda e otimiza em tempo real pra binário nativo da máquina por baixo pra ficar mais rápido. É a técnica que chamamos de JIT ou compilador Just In Time. Javascript faz algo semelhante em navegadores modernos como Safari ou Chrome. Um otimizador em tempo de compilação demora Muito pra compilar, se você já tentou compilar um programa manualmente, dependendo do tamanho já viu que pode levar de segundos a minutos. Uma página web não pode demorar tanto pra carregar. Em vez de tentar otimizar tudo, ele só lê o código fonte em Javascript e faz o mínimo pra interpretar e executar, mas à medida que ele executa um compilador JIT vai otimizando seu código em tempo de execução e só o código que está executando. Se você por acaso carregou uma biblioteca que nunca é usada, o compilador JIT nem vai perder tempo tentando otimizar esse pedaço. Então é tipo um meio termo entre um compilador AOT ou Ahead of Time como em C e um interpretador.</p>
<p>A vantagem do Java compilar num binário intermediário de bytecode e rodar numa JVM é que eliminamos o problema de ter que ter compiladores pra cada arquitetura de computador como Intel ou ARM. Basta que cada tipo de computador e sistema operacional tenha uma JVM. Você já deve ter visto isso com programas Java que rodam em Mac ou Linux ou Windows, mas em cada um você precisa baixar um Java específico uma vez só. Além disso o Java cresceu rapidamente e temos quase tudo escrito em Java. Assim como uma das maiores idéia do C era sua portabilidade para diferente arquiteturas, contanto que você recompilasse o código C, no caso do Java eles também queriam portabilidade escrevendo o máximo possível de tudo somente em Java.</p>
<p>Aliás, essa é outra diferença do Java com interpretadores. Em Java, você tem praticamente tudo que um sistema operacional como Windows ou Linux oferecem mas escritos todos em Java e embutidos dentro da JVM. Em interpretadores como Perl ou Python ou PHP ou Ruby eles dependem bastante do sistema operacional por baixo. Interpretadores de código aberto como eles nunca tiveram a ambição de substituir o sistema operacional, mas de se integrar a eles. Pra fazer isso usamos o conceito de binding, que é uma forma do interpretador expor a uma biblioteca de código nativo o acesso a suas estruturas internas e sua memória. Então, em vez de escrever uma biblioteca de criptografia toda em Python, ele simplesmente mapeia para o que o OpenSSL já instalado no sistema operacional oferece. Perl, PHP e Ruby fazem a mesma coisa. Isso inclusive cria um problema: se a biblioteca nativa que você está fazendo binding não for thread-safe, você bloqueia o interpretador de rodar threads em paralelo. Isso acontece o tempo todo em todos os interpretadores.</p>
<p>Por isso também é mais difícil de fazer interpretadores rodarem em todos os sistemas operacionais. Muitas vezes eles rodam, mas algumas funcionalidades são diferentes ou tem qualidade inferior porque as mesmas dependências não existem em todos os sistemas operacionais. Por isso mesmo, nunca faça programas complexos que rodam num interpretador no Windows esperando que vá funcionar igualzinho em Linux ou Mac. Entre Mac e Linux existe mais compatibilidade porque ambos compartilham muitas das mesmas bibliotecas open source. No Windows elas precisam ser remendadas pra se ligar a bibliotecas proprietárias que só existem no Windows. Portanto, se vai codar em linguagens interpretadas como essas, prefira sempre Linux ou Mac. Não é uma questão de ser anti-Microsoft ou algo assim, os interpretadores que foram criados em Linux funcionam bem só em Linux, mesmo que exista uma versão pra Windows.</p>
<p>Macs passaram a ser construídos sobre um UNIX baseado na Kernel do BSD UNIX quando o OS X saiu em 1999, sistema esse que foi uma evolução do NextStep criado no fim dos anos 80 depois que Steve Jobs foi removido da Apple. Ele é um UNIX de verdade, assim como o Solaris da Sun ou o HP-UX. Eles se parecem com distribuições Linux porque o Linux tentou ser um clone de UNIX. E eles são razoavelmente compatíveis porque os UNIX modernos e distribuições Linux usam mais ou menos as mesmas ferramentas gerais, incluindo o mesmo compilador GCC e suas dependências como o GLIBC. Só o Windows que é completamente diferente dentre os principais sistemas operacionais. E o Java nasceu com a promessa de permitir que você pudesse escrever programas que rodavam em qualquer lugar, porque havia JVM para Linux, para Solaris, pra Mac, pra Windows. Por isso ele se popularizou tanto tão rápido. E também coincidiu com o começo da bolha da internet que acelerou ainda mais a adoção de Java.</p>
<p>Vendo isso, a Microsoft não quis perder o bonde. Primeiro, aproveitando a experiência com Visual Basic e Visual C++ que compilavam binários nativos compatíveis com Windows, eles contrataram o Anders Hejlsberg, criador do Turbo Pascal e do Object Pascal do Delphi da Borland. Seu primeiro produto foi o Visual J++ que era uma versão de Java da Microsoft que compilava pra uma JVM proprietária e com várias funcionalidades incompatíveis. Por exemplo, notável era que o Java também tinha um jeito de fazer bindings pra binários nativos chamado JNI mas a no J++ a Microsoft fez uma forma diferente e discutivelmente melhor ou mais performática chamada RMI e o J++ fazia vários atalhos pra se ligar ao Windows de forma mais eficiente. A linguagem tinha a mesma cara do Java mas não era compatível com o Java da Sun. Por conta disso os programas compilados em J++ rodavam de forma mais eficiente só que o bytecode era incompatível com a JVM da Sun e como pra se chamar Java precisava ser compatível a Sun processou a Microsoft. O J++ acabou sendo descontinuado por conta disso mas as suas sementes é que deram origem ao que você conhece como o CLR o Common Language Runtime, ao framework .NET e à linguagem C#. Por isso as primeiras versões de C#, pra mim, tinham cara de Java misturado com Delphi.</p>
<p>Veja, como a JVM executa bytecodes binários, qualquer linguagem pode ser transformada nesses bytecodes. A sintaxe que conhecemos como Java é só um deles. Por isso hoje em dia você tem linguagens como Scala, Clojure e Kotlin que compilam para o mesmo bytecode e rodam na JVM. Mas a Microsoft usou essa característica antes e além de C# já fez também Visual Basic.NET e ambas compilavam pro bytecode do CLR. O CLR ao contrário da JVM fazia muitos bindinds direto pro sistema operacional pra conseguir ter a melhor performance. Portabilidade não era importante porque bastava rodar onde o Windows rodava, e nessa época a Microsoft ainda considera o mundo open source como inimigo, coisa que só veio a mudar nos anos recentes. Essa é a grande diferença entre Java e .NET. Embora ambas sejam máquinas virtuais e os códigos compilem em bytecodes, o Java tinha como meta rodar no maior número de dispositivos quanto possível e o .NET só em Windows.</p>
<p>Mas as coisas foram mudando. Essa ambição da Sun se mostrou muito a frente do seu tempo. A Sun teve muitas ingerências, perdeu valor e foi comprada pela Oracle e desde antes disso com a depressão depois do crash da bolha da internet em 2001 que ela evolui a passos de tartaruga. Em paralelo no mundo open source, surgiu uma iniciativa ousada. A Microsoft abriu a linguagem C# como um padrão aberto Ecma, então Miguel de Icaza, do projeto GNOME decidiu tentar implementar o CLR no Linux, esse projeto recebeu o nome de Mono. Levou quase 10 anos pra conseguir de fato criar uma CLR e todo o framewortk quase totalmente compatível com o da Microsoft. Porque não era só uma questão de fazer a máquina virtual e o compilador, era necessário também reescrever do zero todas as bibliotecas que compõe o pacote .NET framework.
E muitas delas dependem de bindings para bibliotecas nativas do Windows como já falei. Isso foi um dos maiores problemas pra conseguir compatibilidade no Linux. Por exemplo, as bibliotecas para fazer aplicativos desktop, tipo um Word da vida, usam bibliotecas nativas do Windows pra gerar janelas nativas e não janelas diferentes como o Java fazia com seu toolkit Swing. Do jeito do Java o peso era maior e a performance era menor. Mas usando bibliotecas nativas a velocidade do C# e do .NET pra desenhar as mesmas janelas era muito maior e mais comparável com o que o Visual Basic ou Visual C++ anterior faziam. O Mono no Linux fazia bindings com o GNOME pra também conseguir ter performance similar.</p>
<p>Depois de muitos anos e com a troca de CEO com a saída de Steve Balmer e a entrada de Satya Nadella e sua política de boa vizinhança, a Microsoft se tornou finalmente mais amigável ao mundo open source e acabou comprando a empresa de Miguel De Icaza, a Xamarin. E o Mono foi renomeado como .NET Core e hoje está em caminho de substituir o antigo .NET framework. E apesar do CLR suportar múltiplas linguagens na prática todo mundo programa ou em C# ou em Visual Basic.NET mesmo.</p>
<p>E pro episódio de hoje acho que já cobrimos bastante terreno. Eu tentei explicar um pouco sobre como as coisas funcionam por baixo dos panos. A idéia de CPUs, sistemas operacionais, gerenciamento de processos e de memória. As diferenças entre linguagens compiladas de forma estática e dinâmica. O que são interpretadores e o conceito de máquinas virtuais e bytecodes. E também as diferenças de rodar coisas em paralelo com forks e com threads. Os anos 90 foram o terreno para o crescimento de Java e .NET no mundo mais proprietário e em paralelo pra evolução do mundo open source como um todo, do Linux, e linguagens interpretadas como Perl e PHP. Estamos ainda só começando o século XXI. No próximo episódio vamos precisar do que expliquei aqui pra você finalmente começarem a entender as diferenças entre todas as linguagens modernas.</p>
<p>Como eu já disse no episódio anterior, se eu fiz meu trabalho direito espero que vocês tenham ficado com mais dúvidas do que quando começaram a assistir. Mandem suas dúvidas nos comentários, se curtiram o vídeo mandem um joinha, compartilhem com seus amigos, não deixem de assinar o canal e clicar no sininho. Aguardem a parte 2 sobre tecnologias back end na semana que vem, a gente se vê, até mais!</p>
]]></content:encoded><category>backend</category><category>linguagens-de-programacao</category><category>performance</category><category>akitando</category></item><item><title>[Akitando] #29 - O que eu devo estudar? Vou conseguir emprego?</title><link>https://akitaonrails.github.io/2018/11/27/akitando-29-o-que-eu-devo-estudar-vou-conseguir-emprego/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/11/27/akitando-29-o-que-eu-devo-estudar-vou-conseguir-emprego/</guid><pubDate>Tue, 27 Nov 2018 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/Ll1uAZ-WRa0"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Uma das maiores dúvidas de jovens programadores é sobre que linguagem aprender primeiro. Qual vai ter mais chances de ter emprego e ter mais oportunidades?&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/Ll1uAZ-WRa0"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma das maiores dúvidas de jovens programadores é sobre que linguagem aprender primeiro. Qual vai ter mais chances de ter emprego e ter mais oportunidades?</p>
<p>Parece que se aprender linguagens novas como Go ou Elixir vão ter menos oportunidades, mas porém parecem ser o futuro. Como conciliar aprender linguagens novas que tem menos mercado ao mesmo tempo que trabalhar com linguagens mais tradicionais como Java?</p>
<p>Vou dar meus 2 centavos de perspectiva nesse assunto.</p>
<p>Links:</p>
<ul>
<li>The Pragmatic Bookshelf (<a href="https://pragprog.com/search?q=elixir"target="_blank" rel="noopener">https://pragprog.com/search?q=elixir</a>)</li>
<li>The Little Elixir &amp; OTP Guidebook (<a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">https://www.manning.com/books/the-little-elixir-and-otp-guidebook</a>)</li>
<li>Elixir Sips (<a href="http://elixirsips.com/"target="_blank" rel="noopener">http://elixirsips.com/</a>)</li>
<li>Elixir Weekly (<a href="https://elixirweekly.net/"target="_blank" rel="noopener">https://elixirweekly.net/</a>)</li>
<li>Azul Systems (<a href="https://www.azul.com/careers/llvm-compiler-engineer/"target="_blank" rel="noopener">https://www.azul.com/careers/llvm-compiler-engineer/</a>)</li>
</ul>
<p>AkitaOnRails:</p>
<ul>
<li>[Off-Topic] O Mito do &ldquo;Legado&rdquo; (<a href="http://www.akitaonrails.com/2012/08/15/off-topic-o-mito-do-legado"target="_blank" rel="noopener">http://www.akitaonrails.com/2012/08/15/off-topic-o-mito-do-legado</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Quando comecei este canal eu fiquei com uma dúvida. Se eu deveria me focar nas tecnologias mais novas. Fazer vídeos sobre Javascript ou Elixir ou Kubernetes ou algo assim.</p>
<p>O motivo de porque eu não tenho interesse nisso é porque uma rápida procurada no Google vai te dar muito mais informação, em muito mais detalhes, e muito melhor estruturado do que eu poderia tentar fazer neste canal.</p>
<p>Vamos lá, Se você quiser aprender Elixir, o que você deve fazer? Simples, comece no site elixir-lang.org. Ele tem um excelente tutorial para iniciantes, pra começar a aquecer na linguagem.</p>
<p>Dali você pode ir direto pro site The Pragmatic Programmer. Eles tem diversos excelentes livros sobre Elixir como o Programming Elixir. Outra boa editora é a Manning que tem o clássico The Little Elixir &amp; OTP Guidebook. E tem várias outras que você pode procurar. Na real, o próprio google te dá boas primeiras opções. Tente procurar &ldquo;Best Elixir Books&rdquo; e eis os primeiros resultados:</p>
<p>Você pode começar aprendendo Elixir, Ecto e Phoenix direto e, como eu expliquei em outro vídeo, quando você aprende os primeiros 20% do conhecimento, você já está produtivo o suficiente pra começar a fazer aplicações. Não se engane, você ainda é um iniciante, mas já dá pra começar. Idealmente você vai aprofundar mais, ler mais sobre a história, ir atrás da origem do Erlang e como é o real funcionamento da Beam VM.</p>
<p>Se quiser ir mais devagar, pode assistir screencasts como os do Daily Drip. Eu aprendi muito com os vídeos do Josh Adams. E, claro, assinar mailing lists como o Elixir Weekly pra se manter atualizado.</p>
<p>Daí você quer colocar a aplicação num servidor. Você vai encontrar diversos tutoriais pra Digital Ocean, pra AWS, pro Heroku e assim por diante. E finalmente, se olhar posts do meu blog de 2015, vai ver todas as minhas explicações detalhadas sobre Elixir. Eu aprendi tudo que eu precisava.</p>
<p>E isso sem contar os inúmeros fóruns de reddit a hackernews com mais centenas de dicas de opiniões a respeito.</p>
<p>Ou seja, é relativamente simples aprender o básico de Elixir pra se tornar produtivo.</p>
<p>Se você ainda tem dúvidas o problema não é falta de material, é preguiça de usar o Google direito e de sentar a bunda na cadeira e começar a estudar sozinho.</p>
<p>A pergunta que a maioria das pessoas tem na cabeça é o imediatismo: mas tem emprego pra isso? Será que vou perder meu tempo e não achar nenhum emprego?
E se você começar com essa pergunta toda vez, você está perdendo seu tempo. Porque essa pergunta vale pra Elixir, vale pra Ruby, vale Python, pra qualquer coisa.</p>
<p>Talvez não tenha. Talvez você acabe usando Elixir pra fazer a mesma coisa que faria mais rápido e melhor usando Javascript ou PHP. Talvez você tenha mais opções de emprego se simplesmente aprender C# ou Java.</p>
<p>Agora tudo depende de você. Existem motivos muito objetivos de porque em 2006 eu comecei a evangelizar Ruby mas porque hoje eu não evangelizo Elixir, ou Go, ou Rust. Talvez eu fale sobre isso em outro episódio. Mas vou levantar um ponto: quando eu decidi que ia aprender Ruby E trabalhar com Ruby, havia zero mercado de trabalho no Brasil. Não é um exagero, existia zero empresas usando, existia zero vagas de trabalho, existia zero material em português. Nenhum livro, pequenos fóruns de entusiastas, quase nenhum blog ou site falando a respeito. Esse foi até um dos motivos pra eu ter escolhido Ruby com tanta convicção, embora não tenha sido o motivo principal ;-)</p>
<p>Naquela época eu tinha um emprego muito bom de consultor Java no mundo Enterprise. Eu já ganhava bem pra época, já tinha investido uns 5 anos subindo na carreira de consultor. Mesmo assim eu literalmente joguei tudo fora. Quando eu me coloquei no objetivo do Ruby, eu pedi demissão e comecei do zero.</p>
<p>E calma! Eu não recomendo que ninguém me copie, as circunstâncias foram muito específicas. Eu avaliei com muito cuidado e fiz uma aposta de alto risco. Eu levaria os próximos 10 anos me recuperando, era um plano de longo prazo.</p>
<p>Mas meu ponto é o seguinte: eu decidi aprender Ruby em 2006. Meu primeiro emprego remoto com Ruby foi em 2007. E eu abri minha empresa de Ruby em 2011. Em 2018 minha empresa tem 7 anos, 80 funcionários sendo 75 desenvolvedores
não só fazendo Ruby como Javascript, um pouco de Python e até Java eventualmente. Em nenhum momento minha preocupação principal foi ter ou não ter emprego.</p>
<p>Aprender Ruby ou Aprender Elixir não é o objetivo, é só um meio. Se você acha difícil aprender Ruby, Elixir, Go, Javascript ou qualquer coisa assim, esse é o primeiro ponto que você vai ter que melhorar.</p>
<p>A recomendação que eu daria pra um total iniciante, talvez seja não aprender primeiro nenhuma das linguagens novas. Invista seu tempo em Java ou .NET ou mesmo PHP. Nem todo mundo teve a chance de crescer numa capital como São Paulo, Porto Alegre, Recife. Nós temos opções em lugares assim. Mas se tem uma coisa que eu aprendi percorrendo o país inteiro é que nós somos a exceção.</p>
<p>O normal, principalmente em cidades do interior, em estados do Norte como Roraima, Amapá, é não ter nenhuma opção. Alguns poucos conseguem se mudar pras capitais, mas muitos não tem essa opção. Tem filhos. Tem família pra cuidar.</p>
<p>E pior ainda: em alguns lugares do país não existe internet banda larga que permita um trabalho remoto de qualidade. Obrigado pelo seu desserviço à nação Anatel.</p>
<p>É um dos motivos de porque minha empresa abre fora de capitais. Eu deteste falar da minha própria empresa porque sempre vai soar como propaganda, mas considere que eu tenho 11 escritórios e talvez uns 3 ou 4 funcionários em home office até estabelecermos um novo escritório. Em capitais eu tenho São Paulo, Natal, Teresina e Goiânia.
Os outros são: Santa Maria e Novo Hamburgo no Rio Grande do Sul, arredores de Criciúma em Santa Catarina, Guarapuava no Paraná, Batatais, Sorocaba e Campinas no estado de São Paulo, arredores de Belo Horizonte, e Anápolis em Goiás.</p>
<p>A constatação mais óbvia: existem pessoas de qualidade em qualquer lugar do país. Um programador de São Paulo não é automaticamente superior a um programador de Teresina.
A gente faz o que pode. Mas voltando ao ponto. Eu não faço isso porque sou um divino ser altruísta ou algo assim. Não, eu faço isso porque é lógico: é uma troca voluntária com mútuo benefício.</p>
<p>Voltando ao meu ponto: aprenda Java, aprenda C#, aprenda Javascript ou PHP. Você mora no interior, cidade de 200 mil habitantes, tem uma talvez duas empresas com alguma vaga na área de informática. Talvez seja uma agênciazinha, customizando Wordpress e Magento. Ou alguma instituição pública precisando dar manutenção num sistema velho em .NET. Não importa.</p>
<p>Isso não é pomposo. Não parece divertido. Mas você só pensa assim porque fica assistindo caras como eu falando das maravilhas que empresas de São Francisco usam.</p>
<p>Aqui não é São Francisco ainda. Não faça isso com você. Eu disse num video anterior que nos primeiros 5 anos de carreira acho que nem tinha e eu não participava de eventos, de fóruns, de comunidades, e isso me blindou e evitou que eu perdesse tempo questionando tudo que eu fazia. Questionar só funciona quando você tem alguma experiência. Dos 20 aos 25 eu ainda não tinha. Teria sido um desastre se eu ficasse toda hora correndo atrás do hype. Eu tive a sorte de poder correr atrás só do meu esforço.</p>
<p>Se você se sente mal em entrar numa empresa da sua cidade pra dar manutenção num sistema legado em PHP, você está começando sua carreira em enorme desvantagem, porque você está desperdiçando seu tempo se lamentando. Porque vocês acham que eu não estou fazendo vídeos sobre Elixir mas sobre o ASP que que eu fazia em 2001? Quando eu comecei a aprender Ruby já era 2006, eu já tinha mais de 10 anos de carreira.</p>
<p>Se você vai ficar empacado só no Java velho no sistema legado pra sempre é uma opção sua. Eu não tinha dinheiro na faculdade. Eu não tive dinheiro guardado a maior parte da minha carreira. O que eu fiz? Fiquei culpando a sociedade por não dar chances o suficiente? Não, eu aproveitei cada emprego em que eu entrei, eu fazia o trabalho, mas com o pouco que eu tinha eu comprava livros. Fazia upgrade no meu micro xing ling.
Eu tava fazendo ASP em startup onde eu chegava a varar muitas noites e fazia muita hora extra, mas eu chegava em casa e baixava a JDK e Eclipse e treinava. No episódio sobre o Diário de Henry Jones eu disse que escrevi meu livro de Rails em 2006 enquanto eu estava trabalhando como consultor em Java. Eu escrevi em 2 meses nas horas vagas não porque eu era bom, mas porque eu não tinha outra alternativa.</p>
<p>Prática deliberada? O primeiro segredo é não ter ídolos. O segundo segredo é encarar sua vida inteira como um processo deliberado. Por consequência, o terceiro segredo é jogar o jogo do longo prazo. Hoje eu tenho quase 30 anos de carreira. Eu tive a sorte de começar cedo, aprendi a programar aos 12 anos, eu saí da faculdade mais cedo pra entrar no mercado mais cedo, consciente de todas as desvantagens que eu também ia ter fazendo isso, e eu venho sistematicamente compensando os pontos que eu era ruim.</p>
<p>Eu também queria abrir minha própria empresa antes dos 30 anos. Mas eu esperei. Até os 34 anos. Quando você pratica deliberadamente com seriedade, você sabe no que você é bom e no que você é ruim. Aos 30 eu sabia que ainda não era bom.</p>
<p>As histórias que eu estou contando e as que ainda vou contar, não é só pra você: que nasceu em classe média alta numa capital abastada do país com acesso às melhores universidades e opções. Eu conheci pessoas que vieram de situações muito piores do que as minhas e já me superaram. Cada um tem seus problemas, mas alguns tem o mérito de conseguir superar as expectativas.</p>
<p>Voltando ao Elixir, ou ao Go, ou ao Rust. Não foque em ter um emprego. Esse não é o propósito. Aprenda porque é legal aprender, porque você está praticando algo diferente que contribui pro seu banco de conhecimento e experiência, o único bem que ninguém jamais vai conseguir roubar de você. Dinheiro, posses, tudo isso você pode perder, mas conhecimento e experiência você vai ter pra sempre. E pode ser que você nunca use num projeto. Isso não importa.</p>
<p>Ao mesmo tempo, e esse é o primeiro desafio, evolua seu Java, seu C#. .NET em particular, está bacana hoje em dia, depois que a Xamarin foi adquirida pela Microsoft e eles estão migrando do .NET Framework pra .NET Core, que inclusive roda nativamente não só em Linux como smartphones iOS e Android, o pacote está interessante hoje em dia. No mundo Java você sempre tem a encheção da Oracle, mas alternativas como o próprio OpenJDK ou a nova JDK da Azul Systems, baseado em LLVM, e o dialeto Kotlin da Jetbrains, também aumentaram o padrão do jogo.</p>
<p>O mundo PHP não é o mais atraente mas pelo menos com frameworks como Laravel, você tem opções razoáveis.</p>
<p>E mesmo se você se ver obrigado a usar versões mais antigas por causa do legado da sua empresa, não se aflija.</p>
<p>Deixa eu te contar um dos grandes segredos da nossa área: qualquer imbecil consegue começar um projeto novo do zero. Mas só caras realmente bons conseguem melhorar sistemas legados.</p>
<p>A característica mais importante de qualquer profissional não é saber usar bem suas ferramentas, isso é o básico. É a capacidade de absorver o ambiente, entender as limitações desse ambiente, e tomar as melhores decisões o mais rápido possível e executar. Quem consegue fazer isso sempre vai estar na frente do rato de laboratório que sabe escrever Erlang de cabeça mas não consegue avaliar a situação real e toma decisões caras e de pouco resultado prático.</p>
<p>Eu não escolheria isso de propósito, mas considere que qual o melhor lugar pra aprender essas coisas? Num lugar ruim, com gerentes ruins, com clientes e usuários cabeça duras. Aí você tem 2 escolhas: se conformar e só seguir a inércia e ficar no mesmo lugar, ou aprender a escolher e lutar uma pequena batalha de cada vez, não com o objetivo de salvar a empresa ou o produto ou os usuários, mas pra exercitar sua capacidade de lidar com esses problemas. Era o que eu fazia, por isso eu gostava tanto de consultoria apesar do stress. Eu fui forjado em projetos impossíveis.</p>
<p>Mas, pra sobreviver, você precisa começar sabendo usar as ferramentas mais básicas direito. Exercite seu Go, seu Rust, seu Haskell, nas horas vagas. Continue se exercitando sem parar e sem objetivos de curto prazo. Mas domine a ferramenta do seu trabalho. Você só ganha voz pra tomar decisões de verdade no dia que sua competência técnica é reconhecida porque ela é óbvia. Se você precisa explicar, ela ainda não é óbvia.</p>
<p>Deixe eu contar uma pequena história. Em Dezembro de 2002 a Janeiro de 2003 um dos comerciais da minha consultoria disse que me vendeu pra um trabalho pontual numa grande empresa. Ele tinha ouvido falar que eu sabia ASP - e nessa época eu já estava só em Java - mas o cliente tinha um sisteminha super porco que outra consultoria fez em ASP. Era algum tipo de sistema de RH, pra banco de horas extras ou coisa assim, era tosquinho mesmo. Eu acho que comecei dando suporte por e-mail e depois fiquei alguns dias alocado direto no cliente.</p>
<p>Eu cheguei lá e abri o código. Sério, correram lágrimas de sangue quando eu vi. Fazia tempo que eu não via um código que tinha sido tão maltratado. Era praticamente o guantanamo do código.
Por exemplo, eles tinham feito componentes COM+ que abriam conexão com banco de dados mas não fechavam, tinha vazamentos pra tudo que é lado, era tudo um linguição da pior espécie, com um monte de SQL misturado no meio do HTML. Acho que ele precisava integrar com o SAP mas nem isso funcionava. Felizmente eu era especialista nisso. Enfim, era um desastre. Mas eu me empolguei porque o cliente falou que antes de mim uma outra consultoria tinha vindo tentar consertar e desistiu. E nada aumenta mais minha motivação do que uma tarefa impossível. Não tem nada que tem mais valor do que um problema que ninguém quer nem consegue resolver. Você tem que aprender a gostar de ter as mãos sujas, enlameadas, se arrastar no pântano, e sair vivo no final.</p>
<p>E tinha outra coisa que me chamou a atenção. Quando estavam me mostrando o usuário do sistema disse que quase tudo funcionava, menos uma funcionalidade que não lembro se era um gerador de relatório ou alguma besteira assim. Essa informação era super importante.
Eu disse: repete, é só esse botão aqui, se ele cuspir o que você precisa que é nesse formato x y aqui, pra você tá tudo ok? E ele falou: sim. BINGO</p>
<p>Veja o que é a situação de um consultor: um consultor é um agente externo, nós caímos de para quedas no projeto e na empresa dos outros, pra resolver coisas que ninguém interno sabe resolver, porque senão já teriam resolvido. Nós temos menos conhecimento de tudo, e menos tempo pra resolver o que ninguém conseguiu resolver. É sensacional porque você sempre começa em desvantagem e você é obrigado a fazer mais com menos.</p>
<p>Aquele código era um zumbi ambulante, precisava jogar tudo fora e começar de novo. Reescrever o código todo, eu ia passar os próximos 3 meses lá, fácil. Mas só aquele botão &hellip; eu ajeitei outros bugs críticos ao redor que impediam o sistema de ficar de pé e aí me foquei especificamente naquele maldito botão - nem lembro se era um botão, mas era algo simples nesse nível.</p>
<p>Sei lá, em 1 semana eu consertei. Chamei o usuário e falei pra ele fazer o processo todo. E foi até o fim. HA, um trabalho de 1 mes resolvido antes, e depois que meu concorrente tinha jogado a toalha. Se você nunca passou por isso, passe, não tem nada mais satisfatório do que resolver um problema que outra pessoa disse que não dá pra resolver.</p>
<p>Isso foi em Janeiro de 2003, daí em maio, esse cliente chamou minha consultoria de novo. Os caras gostaram tanto do meu trabalho que decidiram dar a chance da gente participar na RFP ou Request for Proposal deles, que é uma competição entre diversas consultorias. Normalmente só participaria consultoria maior, mas resolveram dar essa chance depois do resultado que eu tinha mostrado. Foram semanas de reunião, eu analisei centenas de páginas de casos de uso, diagramas, que outra consultoria tinha gasto meses escrevendo, escrevi gantt charts, fiz contas, e cheguei numa proposta de trabalho.</p>
<p>Pra vocês verem como isso demora, foi um processo que acho que levou de maio até pelo menos outubro ou novembro. No final eles falaram que ficou caro, me chamaram de última hora pra dizer &ldquo;cara, a gente quer colocar vocês, mas o concorrente ta propondo bem mais barato e ainda ia trazer um consultor gringo&rdquo;. Eu tinha 26 ou 27 anos, o projeto dependia só de eu dizer sim ou não. Eu disse não, eu já estava no limite e apostar mais baixo que isso era garantia de fracasso. Então depois de meses de análise, perdemos a RFP.</p>
<p>Em janeiro de 2004 eles ligam de novo pra gente. Em resumo, o tal consultor gringo acabou não podendo vir e como isso estava no contrato que venceu, o cliente disse: beleza, vou aceitar o gringo não vir, mas então eu exijo que vocês contratem o japonês da outra consultoria. Ha, satisfação receber esse e-mail da concorrente.</p>
<p>O nome desse cliente era Suzano Bahia Sul Papel e Celulose, o projeto era a implementação SAP do módulo de recursos humanos pra gerenciar os mais de 4000 funcionários que eles tinham na época. Meu sub-projeto era criar todos os aplicativos Web J2EE que iam expor todos os serviços como banco de horas, benefícios e tudo mais que seriam acessíveis diretamente pelo funcionários a partir de totens colocados nas fábricas. Incluindo reescrever aquele sisteminha tosco ASP que eu originalmente consertei.</p>
<p>O projeto era de tempo e custo fechado, mas a minha quarteirização negociamos pra ser de hora aberta ;-) e adivinhem o que aconteceu? Eu montei minha própria equipe e passei os próximos 2 anos mais ou menos trabalhando lá. No final levou o tempo e o custo que eu tinha dito que ia levar na RFP. A outra consultoria que era bem maior estava comprando o projeto pra ter um case. No final todo mundo saiu ganhando.</p>
<p>E é assim que as coisas funcionam: eu não planejei nada disso. Se eu tivesse feito bico, esperneado, reclamado, recusado a fazer o servicinho porcaria em ASP porque eu já era um Javeiro hipster ou qualquer besteira assim, nada disso teria acontecido.</p>
<p>É impossível juntar os pontos no presente. Só no futuro que tudo se conecta. Hoje você pensa, se eu ficar fazendo PHP e ganhando só o salário que eu ganho pelos próximos 10 anos, mesmo com aumentos, eu nunca vou pra frente. Mas isso só vai acontecer se você não fizer nada. É impossível dizer o que você vai fazer daqui 10 anos então não faça essa conta, é inútil, é perda de tempo. Em vez de ficar perdendo tempo com essa indagações inúteis, se prepare. Porque quando a oportunidade chegar, é tarde demais, você precisa estar preparado pra pegar a oportunidade quando ela chega.</p>
<p>Todo mundo reclama que ninguém dá oportunidade. É o contrário, existem muitas oportunidades, mas ou você não está preparado ou você está escolhendo as oportunidades que parecem mais confortáveis pra você. Ah vá, isso é mimimi, especialmente na primeira década da sua carreira, tudo é uma boa oportunidade. E eu já disse isso antes: se não está doendo, você não está treinando o suficiente.
Ai, eu não vou evoluir na minha carreira de Java se ficar fazendo esse quebra galho em ASP &hellip; quem diria que isso é o que daria o meu maior projeto em Java até então poucos meses depois?</p>
<p>E vocês? Já foram surpreeendidos no futuro tendo feito coisas no passado que pareciam que não faziam sentido mas em retrospectiva tudo se conectou? Compartilhe com a gente nos comentários abaixo. Se curtiram mandem um joinha, e uma coisa que ia ajudar muito é se vocês ajudassem compartilhando os vídeos do canal pros seus amigos! Não deixem de assinar o canal e clicar no sininho. A gente se vê, até mais!</p>
]]></content:encoded><category>carreira</category><category>aprendizado</category><category>linguagens-de-programacao</category><category>akitando</category></item><item><title>[Akitando] #25 - Sua Linguagem É Especial? Parte 2 em 2001</title><link>https://akitaonrails.github.io/2018/11/08/akitando-25-sua-linguagem-e-especial-parte-2-em-2001/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/11/08/akitando-25-sua-linguagem-e-especial-parte-2-em-2001/</guid><pubDate>Thu, 08 Nov 2018 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/O4CWVQLbi48"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Recapitulando, na série &amp;ldquo;Sua Linguagem NÃO é especial&amp;rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/O4CWVQLbi48"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Recapitulando, na série &ldquo;Sua Linguagem NÃO é especial&rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.</p>
<p>No episódio anterioe eu comecei  a explorar um pouco o outro lado: ninguém disse que você não pode gostar da sua linguagem e, mais importante, dominá-la até o fim.</p>
<p>Veja o que eu fazia quando estava no começo da minha carreira e compare com o que você faz hoje em dia.</p>
<p>Em particular, veja como exploração sem limites pode levar você a antecipar tendências, sem querer.</p>
<p>Quer ver como fazíamos &ldquo;Ajax&rdquo; e &ldquo;NoSQL&rdquo; já em 2001?  :-D</p>
<p>Links de referência:</p>
<ul>
<li>ASP XML Framework (<a href="https://github.com/akitaonrails/ASP-Classic-XML-NoSQL"target="_blank" rel="noopener">https://github.com/akitaonrails/ASP-Classic-XML-NoSQL</a>)</li>
<li>Nested Set (<a href="https://en.wikipedia.org/wiki/Nested_set_model"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Nested_set_model</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No episódio passado eu mostrei um mini-framework que eu fiz com ASP clássico tentando imitar o que, na época, era o moderno ASP.NET Web Forms. Estamos falando de 2001.</p>
<p>Mas alguns meses antes desse framework eu fiz um código que - olhando hoje - é ao mesmo tempo assustador - porque o código é grande e bem feio - mas também surpreendente.</p>
<p>Lembram quando eu falei no episódio de Blockchain rapidamente sobre o Hype do NoSQL em 2010? E se eu te disser que eu já usava noSQL em 2001?</p>
<p>(&hellip;)</p>
<p>Estamos em 2001, como eu expliquei no episódio anterior eu estava desenvolvendo e dando manutenção em diversos sites esportivos de propriedade da PSN como os sites do Corinthians e Cruzeiro aqui no Brasil.</p>
<p>Além disso havia todo um Gerenciador de Conteúdo usado pelos jornalistas pra postar artigos e todo workflow de aprovação e publicação. Tinha área pra subir fotos, ferramentas pra montar pesquisas online tipo polls e muito mais.</p>
<p>Mas tinha 2 problemas que eu queria resolver. Um deles é que antigamente, tanto em ASP ou PHP a gente fazia o HTML misturado com o código. Se você já abriu o código fonte de um Wordpress, bom, era pior que isso.</p>
<p>Olha um exemplo disso, (aponta pro lado) HTML, dados, styles (já tinha CSS mas ele não era tão poderoso como hoje e pouca gente sabia usar direito ainda) e código tudo misturado. Era uma zona.</p>
<p>Uma coisa que tava começando a se popularizar na época era XML. Mas nem todo mundo entendia isso direito ainda. Muito menos no mundo ASP. Assim como hoje em dia pra vocês é tudo JSON, pra mim naquela época o quente era tudo XML.
Tecnologia funciona assim, você não sabe os limites, vai testando.</p>
<p>Então o primeiro problema que eu queria resolver era separar os dados do HTML. Eu não lembro se já existia o framework de Java Apache Coccoon, mas pra quem não sabe ele tinha exatamente esse conceito. Pra quem não sabe o que é XSL é o Extensible Style Language. Hoje XSL é uma família, por exemplo você talvez já tenha ouvido falar de XPATH? Faz parte.</p>
<p>Na época só existia o XSLT que é um padrão de transformação pra modificar um XML em outro XML. Então eu pensei, ora, um exemplo de XML é HTML ou mais especificamente o XHTML que a gente usava na época. Um HTML mais restrito e que é um XML válido. Pensei, porque não separar os dados puros em um XML estruturado e usar transformações XSLT pra converter em HTML?</p>
<p>E foi o que eu fiz. Já que todo site de evento esportivo era basicamente o mesmo template mas com dados diferentes, eu reduzi os dados de cada evento em XML e usava essas transformações pra gerar o HTML final a partir de um template XSLT.</p>
<p>Vou falar que o código que eu fiz ficou tão complicado que só de bater o olho hoje rapidamente nem eu consigo entender tudo que eu fiz. (risos) Quem me conhece sabe que eu costumo dizer que coisas como testes unitários não é uma coisa que você faz pros outros, é pra você mesmo de amanhã. E eu digo essas coisas por experiência, se eu pudesse voltar 17 anos no passado eu daria um tapa na orelha em mim mesmo.
Mas dá um desconto, em 2001, o assunto testes automatizados era meio novo até em Java. Só pra vocês terem uma idéia o site do JUnit só foi lançado 1 ano antes, em 2000.</p>
<p>De qualquer maneira eu meio que consegui fazer isso e pelo menos alguns eventos como Campeonato Paulista, Copa Libertadores, Copa Merconorte, Copa Mercosul, eu acho que consegui implementar nesse framework.</p>
<p>Novamente, eu fiz componentes VBScript encapsulados em Windows Script Components ou WSC. Eu criei componentes com nomes meio bizarros como TXMLSynchro,
TXMLFront, TXMLEvents,
TXMLLanguage e TXMLTranslation.</p>
<p>Ou seja, como eram portais de conteúdo internacionais pra América Latina e Estados Unidos, eu também tinha uma mini-engine de internacionalização. E suportando 2 bancos de dados, SQL Server e Oracle.</p>
<p>E porque tudo começa com T? Porque eu era desenvolvedor de Delphi alguns anos antes. E em Delphi todos os componentes começavam com a letra T e eu acabei pegando esse hábito de começar tudo com T. Em Delphi existia essa convenção porque tudo que definia um Tipo começava com T.</p>
<p>O componente principal TXMLSynchro usava uma convenção de pastas e ele ia percorrendo as pastas em busca dos XML e os templates XSL pra ir gerando os HTML. Esse conceito soa familiar? Se você já usou Jekyll por exemplo, é parecido, só que em vez de ser Markdown, era XML. Então eu tinha um gerador de site em XML em 2001.</p>
<p>Na verdade, era um passo além. Os jornalistas podiam editar o conteúdo via um Admin que gravava o XML no banco de dados. Daí eu pegava esse XML e aí convertia pra HTML via o XSL, por isso era TXML Synchro o nome do componente.</p>
<p>Ao mesmo tempo esse pacote era um gerador de eventos, daí o outro componente TXML Event, daí cada nova Copa ou evento era mais ou menos simples de gerar tudo.</p>
<p>Agora vem o principal. Eu gostei desse conceito de gravar XML natabela. E fui um passo além. Tinha um outro problema em todos os eventos esportivos, todo campeonato era visualizado via uma chave. Sabe? Basicamente uma árvore de nós convergindo na semi-final, final até o campeão.</p>
<p>Mas todo evento tem chaves diferentes. O problema: como eu represento isso num sistema multi-eventos?</p>
<p>A primeira coisa que você provavelmente teria vontade de fazer é criar uma tabela Evento, uma tabela Jogo e uma tabela Time ou algo assim e ir criando uma linha no banco pra cada nó da árvore. Isso é um design terrível. É o pattern que chamamos de Parent-Child. Terrível porque não tem como trazer todos os nós da árvore num único select.</p>
<p>Árvore é uma das coisas ruins de se mapear numa tabela SQL. O jeito correto é usar um Nested Set, vou deixar o link abaixo se você nunca ouviu falar em Nested Sets. Mas em 2001 eu também não conhecia isso, só conhecia o Parent-Child que é o que eu falei agora que é ruim, e pelo menos eu sabia disso.</p>
<p>Aqui foi um insight. Gerenciar nós de uma árvore, em SQL não é trivial. Gerenciar nós num XML era fácil, é um DOM, como o DOM que você manipula hoje via Javascript num HTML, é uma árvore. CreateElement, Sibling, Child, etc. Mas não existe banco de dados XML, não em 2001.</p>
<p>Mas, bancos SQL tem campo BLOB. E se eu simplesmente gravar o XML num campo blob? Daí com um único select eu puxo o XML inteiro que representa a árvore toda e posso passar num DOM Document pra parsear e criar os objetos todos.</p>
<p>E se eu for além, e se eu usar XSL em cima do XML que veio do banco pra gerar o HTML final que o jornalista ia ver no gerenciador de conteúdo?</p>
<p>E se eu for além, e se eu usar esse troço novo que o Internet Explorer 4 tinha inventado uns 3 anos antes chamado Dynamic HTML que me permite modificar o HTML dinamicamente na página via Javascript? Cês tão vendo? Eu to falando de tecnologia de ponta aqui.</p>
<p>E se eu somar isso a esse componente novo chamado MSXML que eu podia instalar tanto no servidor mas tinha componente pro browser. Isso permite, via Javascript ou VBScript, fazer uma requisição HTTP pra aplicação ASP me devolver o XML e, no client-side, eu poderia via javascript parsear com MSXML e ter o DOM na memória?</p>
<p>Agora se eu somar tudo isso. Eu posso fazer o jornalista clicar em qualquer nó da árvore, editar o nome do time, pontuação e tudo mais direto na representação do DOM do XML na memória do browser. Daí via Javascript gerar o XML que representa essa árvore e dar POST pro servidor, que daí ia gravar direto na tabela do banco.</p>
<p>O que eu tinha acabado de fazer?</p>
<p>Asynchronous Javascript and XML ou AJAX, nome que seria cunhado só em 2005, 4 anos depois. Eu não fui o primeiro a fazer isso, muito antes do Google fazer o Gmail que popularizou a técnica, a Microsoft já tinha o Outlook Web App em 2000 que era basicamente um Outlook versão Web que usava o componente XMLHTTP, que é o precursor do XMLHttpRequest. Ninguém usa esse objeto direto, hoje em dia vocês usam o que? Axios? Mas por baixo é o mesmo XMLHttpRequest. Esse componente nasceu pela primeira vez em 1997 ou 98 com o Internet Explorer 4, mas acho que só começou a ser usado de verdade com o IE 5 em 1999.</p>
<p>E isso de serializar e deserializar árvores de objetos XML num campo blob numa tabela é um tipo rudimentar de NoSQL. É o que hoje a gente faz com Postgres e o tipo de campo JSONB. É basicamente a mesma coisa: em vez de fazer um monte de tabela pra cada campeonato, eu reduzi tudo numa tabela só sem schema, schemaless.</p>
<p>O termo NoSQL foi cunhado primeiro em 1998 mas só se popularizou quando a Last.fm reintroduziu o termo em 2009, 8 anos depois do que eu fiz.</p>
<p>Imaginem minha surpresa quando em 2005 eu vi todo mundo ficando doido com o conceito de Ajax e em 2009 todo mundo ficando doido com o conceito de NoSQL. Eu juro que coçava a cabeça pensando, é legal, mas isso é velho não?</p>
<p>Lembrando que em 2001 não tinha ainda Hacker News, nem Reddit, nem Quora, nem Stackoveflow. Esses conceitos nasceram organicamente combinando as peças que eu aprendi fora da caixa e fazendo o que todo programador novo faz: over engineering.</p>
<p>Engraçado que eu tinha noção que a solução que eu fiz era bem overkill e over engineering mesmo pra época, mas assim como muitos de vocês, é aquelas coisas que você faz mais porque é divertido e menos porque é um bom custo-benefício.</p>
<p>O objetivo dessa história é ilustrar como sem querer eu cheguei nas mesmas conclusões que outras pessoas chegaram de outra forma. Acho que muitas outras pessoas usavam da mesma maneira, mas não era tão famoso porque não tinha blogs, não tinha meetups, não tinha screencasts nem nada disso em 2001. Se você não prestar tanta atenção na propaganda, ou a forma como os outros dizem que você deve usar essa ou aquela ferramenta e encarar tudo como peça de lego eventualmente você mesmo vai chegar nas mesmas conclusões que os grandes nomes chegam.</p>
<p>O que eu falo mais é o seguinte: se você está seguindo o que os outros estão dizendo, você já está atrasado. Eu não sigo muito os outros, eu penso: pra que esse troço serve ou não serve. Em vez de seguir o que os outros dizem “ah, isso só dá pra fazer se for em Java” ou “ah, isso o melhor é se for em Javascript”. Eu penso. Quem disse? Deixa eu testar. 99.9% das vezes não funciona, mas essa é justamente a graça do negócio. Programação não é pra ser um negócio que todo código que vc digita precisa necessariamente ser útil pra alguma coisa. É como dizer que toda vez que você for chutar pro gol precisa fazer gol. Senão nem vale a pena tentar chutar. Se futebol fosse só isso, não teria graça, teria? E fazendo assim, um vez por década você tem um momento eureka!</p>
<p>E você, já teve seu momento eureka em algum momento? Compartilhe com a gente sua história nos comentários abaixo. Se curtiram o vídeo mandem um joinha, assinem o canal e cliquem no sininho! A gente se vê, até mais!</p>
]]></content:encoded><category>desenvolvimento-web</category><category>arquitetura-de-software</category><category>linguagens-de-programacao</category><category>akitando</category></item><item><title>[Akitando] #16 - Sua Linguagem NÃO É Especial! (Parte 2)</title><link>https://akitaonrails.github.io/2018/10/04/akitando-16-sua-linguagem-nao-e-especial-parte-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/10/04/akitando-16-sua-linguagem-nao-e-especial-parte-2/</guid><pubDate>Thu, 04 Oct 2018 17:00:00 -0300</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/XcTTajFENHI"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Depois da semana geek, vamos &amp;ldquo;geekar&amp;rdquo; um pouco na programação também.&lt;/p&gt;
&lt;p&gt;Honestamente essa polarização em torno de linguagens de programação é super cansativa. A turma do funcional vs a turma dos objetos. A turma do javascript, a turma do Go.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/XcTTajFENHI"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Depois da semana geek, vamos &ldquo;geekar&rdquo; um pouco na programação também.</p>
<p>Honestamente essa polarização em torno de linguagens de programação é super cansativa. A turma do funcional vs a turma dos objetos. A turma do javascript, a turma do Go.</p>
<p>Enquanto isso, a grande maioria da população de programadores está aflita, sem saber pra onde correr. Quantas linguagens preciso aprender? 2? 6? 50???</p>
<p>Calma! Vamos começar a colocar ordem nessa bagunça. Hoje vou explicar como fazer isso e porque sua linguagem de programação favorita não é tão especial quanto você pensa!</p>
<p>Não deixe de assistir a Parte 1 antes!</p>
<ul>
<li>Genealogia (<a href="https://github.com/akitaonrails/computer_languages_genealogy_graphs"target="_blank" rel="noopener">https://github.com/akitaonrails/computer_languages_genealogy_graphs</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Vamos continuar a história que iniciei no episódio anterior. Se você Não assistiu clique no link acima e veja a parte 1, vamos lá, eu espero.</p>
<p>Já viu? Então vamos continuar nossa história das linguagens.</p>
<p>(..)</p>
<p>No episódio anterior falamos da linhagem de linguagens derivadas de ALGOL, que eu chamaria da linguagem A, mas existiu outra A.</p>
<p>Já que falamos em Fortran e linguagem B, existiu uma outra vertente que quis perseguir sintaxes diferentes de notação matemática para se adequar mais a processamento de dados de negócios. O time da famosa Grace Hopper foi um dos que perseguiu esse objetivo entre 1958 e 1959. Vocês devem estar se lembrando: Ahá sim, daí ela inventou o COBOL. BEEE Nope, ela inventou outra linguagem ANTES do COBOL que viria a ser a primeira linguagem focada em se expressar mais próximo do inglês, esse foi FLOW-MATIC também conhecido como B-0 ou Business Language versão 0. Achou B-0 um nome estranho? É porque antes disso Grace Hopper fez extensões pra uma linguagem chamada ARITH-MATIC ou A-2. e antes do A-2 tinha o A-1 e, claro o A-0 também escrito por Hopper, isso lá pra 1951. Então a linhagem completa começa em A-0, A-2, A-3 que são ARITH-MATIC, AT-3 que é MATH-MATIC e B-0 que é FLOW-MATIC antes do COBOL. Todas as anteriores que falei antes são de Hopper, o COBOL não é, ele é inspirado nos trabalhos anteriores de Hopper. Todo mundo decorou COBOL e Hopper mas ninguém nunca ouviu falar dessa história. Não só decorem, busquem as fontes!</p>
<p>Já que mencionei Java e C# muita gente acredita que Java que inventou orientação a objetos, garbage collector, tipagem forte. Bom, vocês já viram que no fim dos anos 50 você já tinha ALGOL, Pascal que tinham tipagem forte. Alguns de vocês talvez tenham ouvido falar da linguagem Simula de 1967 criada por Ole-Johan Dahl e Krysten Nygaard, também chamado de Simulation Language que foi quem introduziu os conceitos de objetos, classes, herança, sub classes, co-rotinas e também o garbage collector. Mas não foi o primeiro, garbage collector. Linguagens como Basic já tinham um tipo primitivo de garbage collector. Mas essa é a parte fácil. Quantos de vocês já ouviram falar numa linguagem chamada CLU?</p>
<p>E eu disse CLU, CE ELE U, só pra garantir. Essa linguagem foi criada no MIT em 1974 por ninguém menos que Barbara Liskov e seus alunos. Quem? Bom, se você é de Java ou C# ou já estudou Uncle Bob conhece o princípio SOLID de orientação a objetos. S de Single Responsability Principle, O de Open/Closed Principle e L de Liskov Substitution Principle. Aí você que nunca parou pra pensar, o que diabos é Liskov? Prazer, Barbara Liskov criadora da linguagem CLU que fez adições no paradigma de programação orientada a objetos como tipos de dados abstratos, iteradores, retorno de múltiplos valores ou parallel assignment, classes com construtores. CLU, pra variar, tem sintaxe derivada de quem? Yep, ALGOL. E se chama CLU porque ele lida com Clusters que é mais ou menos uma Classe hoje.</p>
<p>Falando em funcionalidades, que tal falar sobre regular expressions. Todo mundo conhece hoje em dia né? Toda linguagem que se preza tem uma biblioteca boa de regex, normalmente baseada no padrão do Perl 5. Mas de onde diabos vem aquela maldita sintaxe de regex? Volte pra 1962 e vai encontrar o String Oriented and Symbolic Language ou SNOBOL, uma série de linguagens da Bell Labs - de novo - que culmina no SNOBOL 4, uma linguagem voltada a string de texto e que se diferencia das outras linguagens por ter patterns como first-class citizens. Resumindo a história era uma linguagem muito usada pra manipular texto mas foi caindo em desuso à medida que nos anos 80 e 90 surgiram AWK e o próprio Perl. Mas os patterns de SNOBOL ainda são mais poderosos do que regex hoje, meio como HTML é um sub-caso de SGML ou XML. Lembra do Backus-Nauer Form ou BNF que dá origem à definição de gramática de compiladores? O SNOBOL são quase isso pra patterns. Guarde SNOBOL e vamos pular pra outra linguagem</p>
<p>Nos anos 70 surgiu outra linguagem, agora uma funcional chamada Hope na universidade de edinburgh. Ela é anterior à Miranda ou Haskell e é contemporânea do da família ML de linguagens funcionais, sendo derivada do NPL de 77. E Hope e NPL são notáveis por serem as primeiras linguagens a terem o conceito de call-by-pattern. Guarde essa informação.</p>
<p>Em 1972 você tem a linguagem PROLOG que é um animal muito exótico, finalmente alguma coisa não derivada de ALGOL. Ela tem um paradigma que não é funcional, não é objeto, não é imperativo. É programação lógica, uma linguagem declarativa. Você começa definindo fatos e regras. Tudo é expresso em termos de relações ou relations hoje em dia pense em algo como um SQL. e a computação são queries nas relations. Na teoria de Codd sobre SQL ele se baseia em cálculo relacional - você estudou isso na faculdade né? E o que eu falei de fatos em prolog são dados. Uma relation em teoria relacional é a mesma coisa que uma tabela em SQL ou função relacional em lógica predicativa e a mesma coisa que um tuple-set em teoria dos conjutnso. E uma view de SQL é equivalente a uma regra de prolog.</p>
<p>Mais do que isso, prolog também é uma linguagem, como Hope e NPL baseada em pattern-matching. Mais especificamente, você podia implementar o pattern matching de Snobol em Prolog. Aliás, como Prolog não tem sintaxe derivada de Algol ela é bem feia aos nossos olhos acostumados à linguagem de Algol como Java ou C# ou Python. Tem uma outra linguagem que herda partes dessa sintaxe feia de Prolog, com um sistema de pattern-matching como Snobol e call-by-pattern como Hope.</p>
<p>Muito prazer, seu nome é Erlang, lançado em 1986 por Joe Armstrong. Graças aos céus o José Valim inventou a linguagem Elixir que compila no mesmo bytecode de Erlang. Vocês devem conhecer Elixir e Erlang primeiro como uma linguagem funcional e segundo como uma linguagem de alta concorrência.</p>
<p>Vamos lá, primeiro ela não é uma linguagem puramente funcional. Aliás, Javascript também não é puramente funcional. Elas tem funções, ponto. Funcional são outras linguagens. Alguns de vocês que leram muitos blogs vão correr pra dizer “sim, como Haskell”. Vou mais pra trás, quando falei de Hope eu falei de Miranda que veio depois, que é uma linguagem lazy, puramente funcional, que não tem efeitos colaterais, e é escrita como uma série de equações que define várias funções matemáticas e tipos de dados algebraicos. De curiosidade, o parsing da linguagem faz uso de identação de forma a não precisar de chaves pra delimitar statements. Essa funcionalidade foi inspirada por … aha Python! Você poderia dizer. Não, Python é um bebê, antes dele tivemos pelo menos ISWIM e OCCAM.</p>
<p>ISWIM?  Sim, de 1966, inspirado por … bingo, ALGOL. Na verdade ALGOL 60 e Lisp. ISWIM significa If you See What I Mean ou também I see what you mean, mas isso seria I S W Y M mas ai foi escrito errado como I S W I M ou ISWIM. Ela é mais uma descrição de uma linguagem e não foi implementada, mas ela suas idéias inspiraram a linguagem Hope, depois Miranda, daí Haskell e Clean. Clean que tem uma relação de ser inspirado por Haskell e inspirar Haskell sendo meio um concorrente e ter uma funcionalidade em particular que compete com os monads de Haslell, no caso de Clean ele lida com estado mutável e I/O através de um uniqueness typing system.</p>
<p>Mas além de ISWIM ter código identado que veio a inspirar Python, a outra linguagem desse jeito é OCCAM. E ela também se relaciona com Erlang porque antes de Erlang foi desenhada como uma linguagem focada em concorrência e implementa um troço chamado Communicating Sequential Processes ou CSP. Basicamente a comunicação entre processos através de channels ou canais. O CSP de Occam foi reciclado para um aspecto mais funcional no Erlang através de passagem de mensagens entre processos. E você deve ter ouvido falar de outra linguagem que ficou famoso na atualidade por redescobrir CSP, sim a linguagem Go e suas go-rotinas. Todo mundo que fala de CSP menciona Tony Hoare mas eu nunca vi ninguém lembrar que Hoare inventou OCCAM e foi onde ele implementou CSP primeiro.</p>
<p>E já que mencionei a família ML de linguagens funcionais vale lembrar de uma coisa que surgiu primeiro nessa família lá no começo dos anos 70. Um troço chamado Hindley-Milner type system, um sistema de tipos clássicos de lambda calculus com polimorfismo paramétrico. Você viu isso na faculdade né? E porque isso é interessante?  O que as linguagens Clean, Crystal, D, F#, Go, Haskell, Java a partir da versão 10, Julia, Kotlin, OCaml, Rust, Scala, Swift e Visual Basic tem em comum? Eles tem tipagem forte mas diferente do Java antigo onde você precisava declarar todos os tipos de todas as variáveis explicitamente hoje em dia você não precisa né? E nem por isso a linguagem tem tipagem dinâmica como Python ou Ruby, ela tem tipagem forte através de Inferência de Tipo. Outro nome para inferência de tipo? Hindley-Milner type system. Muito prazer, linguagens ML.</p>
<p>E pra terminar, se você é de Javascript tem mais um aspecto dessa linguagem que não é novo. Que tal JSON ou Javascript Object Notation? Nosso querido Douglas Crockford foi olhar nas páginas da história de outra linguagem, o Relative Expression Based Object Language ou REBOL que é contemporâneo de 1997 desenhado por Carl Sassenrath primariamente feito pro AmigaOS e que usa conceitos de programação de Lisp, Forth, Logo e Self. Antes de Ruby tornar popular o conceito de DSLs ou Domain Specific Languages, é REBOL que foi desenhado especificamente pra DSLs, particularmente pra toolkits gráficos de Amiga.</p>
<p>Você acha que existem quantos paradigmas de programação? Muita gente fica se gabando achando que sabe muito discutindo porque o paradigma funcional é superior ao paradigma de objetos que se acha superior ao paradigma imperativo ou procedural. Meh, vá ver a história da linguagem APL e outras do paradigma de array programming e vai encontrar linguagens boas pra calcular vetores como Mathematica ou o moderno Julia. Se você prestou atenção na minha história já sabe que Prolog é paradigma lógico e declarativo. O mundo é sempre muito maior do que você pensa. Muito maior do que seu Javascript.</p>
<p>Mais ainda, na sua cabeça você acha que Java é velho? Agora que ele tem pouco mais de 20 anos? Fortran é de 1957 e tem versões novas sendo usadas até hoje. Cobol tem versão até hoje e é de 1959 ele inclusive ganhou novas funcionalidades como orientação a objetos. E você acha que elas não são usadas mais? Sabe um dos exemplos que todo mundo adora usar pra falar de Python, a biblioteca de ferramentas matemáticas ScyPy? Sabe porque diabos ela é tão rápida? E se eu te disser que mais de 20% do código fonte de Scypy é C outros 30% do código fonte é FORTRAN! 45% é uma casca de Python pra falar com todo esse C e Fortran. E é uma excelente escolha, porque Python sozinho nunca iria ter tanta performance pra cálculo numérico como Fortran.</p>
<p>Estão entendendo? Saiam do casulo. Pensem fora da caixa. Sua linguagem não foi desenhada por um ser onipresente e onipotente que um belo dia acordou e disse “que se faça Javascript” ou “que se faça Python”. Se você é programador de C# e fica zombando de Delphi, que tal saber que elas vieram do mesmo autor e derivam das mesmas linguagens? Você acha que porque usa Go ou Elixir de repente só recentemente surgiu programação concorrente com processos e channels de CSP? Nunca foram procurar Tony Hoare e linguagens como Occam? Pattern matching, pfff, SNOBOL.</p>
<p>O que eu falei nesses 2 episódios é apenas uma dimensão da história da programação. Em outors episódios vou mostrar como você junta esta dimensão com outras dimensões pra começar a entender as fundações não só de onde vieram suas linguagens favoritas mas o que elas poderão ser no futuro.</p>
<p>O que acharam dessa série em 2 partes? Não deixe de colocar nos comentários o que acharam. Se gostaram mandem um joinha. Assinem o canal e não deixem de clicar no sininho pra não perder os próximos episódios. A gente se vê, até mais!</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>ciencia-da-computacao</category><category>programacao</category><category>akitando</category></item><item><title>[Akitando] #15 - Sua Linguagem NÃO É Especial! (Parte 1)</title><link>https://akitaonrails.github.io/2018/10/02/akitando-15-sua-linguagem-nao-e-especial-parte-1/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/10/02/akitando-15-sua-linguagem-nao-e-especial-parte-1/</guid><pubDate>Tue, 02 Oct 2018 17:00:00 -0300</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/p9-WuJbVHHc"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Depois da semana geek, vamos &amp;ldquo;geekar&amp;rdquo; um pouco na programação também.&lt;/p&gt;
&lt;p&gt;Honestamente essa polarização em torno de linguagens de programação é super cansativa. A turma do funcional vs a turma dos objetos. A turma do javascript, a turma do Go.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/p9-WuJbVHHc"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Depois da semana geek, vamos &ldquo;geekar&rdquo; um pouco na programação também.</p>
<p>Honestamente essa polarização em torno de linguagens de programação é super cansativa. A turma do funcional vs a turma dos objetos. A turma do javascript, a turma do Go.</p>
<p>Enquanto isso, a grande maioria da população de programadores está aflita, sem saber pra onde correr. Quantas linguagens preciso aprender? 2? 6? 50???</p>
<p>Calma! Vamos começar a colocar ordem nessa bagunça. Hoje vou explicar como fazer isso e porque sua linguagem de programação favorita não é tão especial quanto você pensa!</p>
<p>Veja hoje a Parte 1 e não deixe de assinar o canal para ver a Parte 2!</p>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Esta semana vamos pular pra assuntos de tecnologia. Mas eu quero colocar em prática o que eu disse semana passada nos episódios geek. Lembra como eu disse que nós geeks não só assistimos as coisas mas gostamos de ficar explorando detalhes, rotas alternativas e até re-editando o que gostamos?</p>
<p>Hoje eu vou mostrar como fazemos isso em tecnologia também. Muitos de vocês hoje devem ter linguagens de programação favoritas. Todo mundo tem pelo menos uma. Como muitos de vocês sabem, eu gosto muito da linguagem Ruby. Porém isso não me torna cego às demais linguagens e eu não digo isso só da boca pra fora.</p>
<p>Também como disse no episódio de faculdade, eu comecei no fim dos anos 80, passei por Basic, dBase III, Clipper, Turbo Pascal 6, Visual Basic 6, Delphi desde a versão 1.0, Perl desde a versão 5 - se bem que ela nunca saiu da 5, PHP desde a 3, ASP clássico, .NET C# e VB.NET desde a versão 1.0, Java desde a versão 1.0, Javascript desde antes de virar padrão ECMA, Python 2. E isso foi só até o fim dos anos 90.</p>
<p>Se tem uma coisa que vocês devem levar deste episódio é o seguinte: linguagens de programação Não são filosofias de vida. Linguagens de programação são meras ferramentas. Ferramentas foram feitas para servir seus mestres, e não o contrário. Linguagens de programação não merecem lealdade e definitivamente não são religiões. Eu não sou definido pela minha linguagem de programação favorita.
Você não é sua linguagem de programação.</p>
<p>(&hellip;)</p>
<p>(crack de dedos) É hora de mostrar aqui quem manda.</p>
<p>A melhor forma de desmistificar qualquer coisa é abrir e dissecar. Ver do que é feito. Ver quem fez. Ver de onde veio. Pare de colocar as coisas num pedestal só porque você não compreende. Nada merece ficar num pedestal, muito menos linguagens de programação.</p>
<p>Antes de mais nada, se eu te perguntar, quais todas as linguagens de programação que existem hoje? Você talvez tenha a seguinte imagem na cabeça (IMAGEM)</p>
<p>Mas a realidade é que esta é a lista de linguagens ATIVAS nesse momento.
Pois é, bem mais do que você imaginava. Existem linguagens em uso neste momento que você sequer ouviu falar. Você precisa expandir seus horizontes. E agora o mundo que já parecia grande parece impossível. Mas não se preocupe, vamos organizar isso.</p>
<p>Se você só conhece uma ou duas linguagens, ou mesmo meia dúzia, faz parecer que cada uma delas é genial e única. Faz parecer que nunca mais ninguém vai chegar numa combinação tão perfeita. Por exemplo, a orientação a protótipos de Javascript, os CSP de Go, o garbage collector do Java, o pattern matching do Elixir.</p>
<p>Vamos começar do começo. Muitos quando pensam em linguagens antigas pensam em Fortran ou Lisp, mas eu começaria por Speedcode ou Speedcoding do lendário John Backus. É uma das primeiras linguagens interpretadas de alto nível. Muita gente acha que COBOL é quem inaugurou a era das linguagens simbólicas que prezava pela facilidade de uso ao usar palavras em vez de mnemônicos como Assembly, como o próprio nome Speed code já diz. Estamos falando de 1953. O objetivo foi facilitar a computação numérica de ponto flutuante para calcular posições astronômicas, trocando facilidade de uso por performance.</p>
<p>O mesmo John Backus depois criaria a linguagem mais famosa dele e que ainda está em uso até hoje, o Formula Translator ou Fortran, de 1957. Agora você sabe que a idéia original veio de Speedcoding. Como disse antes Speedcoding era interpretado e embora com uma linguagem fácil de usar a performance era sofrível, até 20x pior que assembly de máquina e naquela época hardware era proibitivamente caro. Por isso pense em Fortran como uma evolução compilada de Speedcode. Embora uma linguagem de uso geral obviamente ele foi pensado como uma linguagem para cálculo númerico. Se você fez faculdade e aprendeu sobre montadores, aprendeu sobre assembly.</p>
<p>De bônus, muitos ainda pensam que Fortran gera binário mais rápido do que C. Isso foi verdade no começo. Depois do padrão C99 ter colocado o keyword restrict isso já não é mais verdade. A diferença é que Fortran não permite aliasing de pointers. Dois nomes apontando pro mesmo espaço de memória e fazendo overlap. Se você usar restrict em C o mesmo código vai gerar um binário igualmente rápido.</p>
<p>Seguindo a história, você tem o Algorithmic Language ou ALGOL que é uma família de linguagens imperativas. Como o nome diz foi usado para descrever algoritmos, uma evolução do Fortran em termo de linguagem de alto nível. Uma das coisas mais importantes da produção do ALGOL foi a invenção do Backus Normal Form por John Backus de novo que depois foi revisado e extendido por Peter Naur pro ALGOL 60 e renomeado como Backus-Naur Form ou BNF por sugestão do lendário Donald Knuth. BNF é uma técnica de notação para gramáticas livre de contexto usado para descrever e definir sintaxe de linguagens. Se você fez faculdade e aprendeu sobre compiladores então teve que lidar com o Yet Another Compiler-Compiler ou YACC que é padrão em BSD ou o GNU bison.</p>
<p>Se seguirmos para o começo dos anos 1960 encontramos o Combined Programming Language também chamado de Cambridge Programming Language por ter sido desenvolvido na universidade de Cambridge ou Christopher’s Programming Language por ter sido desenhado por Christopher Strachey junto com David Barron. Na prática uma linguagem inspirada em ALGOL 60. Mas a linguagem demorou muito pra ficar pronta, quis fazer muita coisa, ele queria ao mesmo tempo possibilitar programação de baixo nível e ter abstrações de alto nível. No final não foi pra lugar nenhum mas inspirou um derivado mais simples chamado BCPL ou Basic CPL ou também Boostrap CPL voltado mais pra programação de sistemas, particularmente pra escrever compiladores.</p>
<p>A família de linguagens ALGOL daria origem a derivados em particular alguns que você não imaginaria. Niklaus Wirth e Tony Hoare propuseram o ALGOL W como sucessor do ALGOL 60. Esse trabalho inspiraria Wirth a criar uma nova linguagem desenhada pra ser eficiente em tempo de compilação e execução, fortemente tipada, que possibilitaria a criação de estruturas de dados complexas e seria boa para ensinar programação estruturada e algoritmos, e entre 68 e 69 nasce a linguagem Pascal. Sim, Pascal é um derivado direto de ALGOL. Eu aprendi algoritmos e estruturas de dados com esse livro de Wirth.</p>
<p>Dando uma tangente nessa história, nos anos 80 Anders Hejlsberg escreveu o compilador Blue Label Pascal. Depois de ser adquirido pela Borland isso se tornaria o Turbo Pascal, que eu mencionei em outro episódio que foi a linguagem que aprendi na faculdade. Turbo Pascal fez muito sucesso na época, era muito compacto, eficiente e ainda vinha com uma IDE. Muitos programas famosos do Macintosh original foram escritos num dialeto de Turbo Pascal. E da Apple Computer saiu a extensão Object Pascal. É parecido mas não é a mesma coisa que o Object Pascal da Borland que viria a se tornar o famoso Delphi.</p>
<p>Quem contratou Anders Hejlsberg foi a Microsoft e ele criou o Microsoft J++, o derivado de Java da Microsoft. Eu costumo dizer que o atual C# que foi desenhado pelo Anders é o filho bastardo de Delphi com J++.</p>
<p>Voltando ao ALGOL e ao BCPL, como você pode imaginar, é bem adequado que da linguagem ALGOL ou A, venha BCPL que na sequência coincidentemente deu origem à linguagem B por volta de 1969 desenhada pelos lendários Ken Thompson e Dennis Ritchie na Bell Labs. O BCPL, CPL, ALGOL tem cara de Pascal pelos motivos que falei acima, mas Thompson e Ritchie reverteram muita coisa da sintaxe. Por exemplo, em vez de atribuição ser dois pontos e igual passou a ser só igual, eles inventaram os operadores aritméticos de atribuição que é o mais igual ou menos igual além de incrementadores e decrementadores que são os operadores mais mais e menos menos. Enquanto Pascal virou um derivado de tipagem forte de Algol, o B foi feito pra ser sem tipo. Agora adicione a isso a sintaxe de chaves e você tem a próxima linguagem desenhada por Thompsom e Ritchie para suceder o B, a linguagem C.</p>
<p>Agora você sabe de onde veio C e como Pascal é seu primo e como Java e C# são netos bastardos e primos bastardos um do outro, é um verdadeiro bacanal de linguagens.</p>
<p>Mas isso não pára por aqui. Sabe quem mais trabalhou na evolução do ALGOL, o ALGOL 68? Um cara chamado Stephen Richard Bourne, que criou um troço chamado Bourne Shell pra substituir o Thompson Shell ou SH. A linguagem de script é inspirado em ALGOL 68. Falando em shells David Korn criou o Korn Shell ou KSH baseado no Bourne Shell e é um meio do caminho entre ele e o C Shell de Bill Joy - talvez vocês de Java lembrem dele da época da Sun Microsystems. Agora some Bourne Shell, C Shell e Korn Shell implementado no mundo GNU e você tem o Bourne Again Shell ou BASH! Agora você sabe de onde vem o BASH. E só pra finalizar essa tangente, se você pegar BASH, KSH e o TC Shell sai o mais moderno Z Shell que muitos preferem usar hoje. Se você vem de UNIX de verdade e sistemas BSD antigos deve ter encontrado o Bourne Shell original.</p>
<p>Ufa, essa história é bem longa. Mas depois dessa tangente em shells vamos parar por aqui. No próximo episódio eu vou finalizar essa história mas eu acho que você já entendeu onde eu quero chegar.</p>
<p>O mundo de programação é muito maior do que sua linguagem favorita. E linguagens não são estáticos. Elas não nasceram do nada por um criador místico. Todas são derivadas de alguma outra e sabendo de onde elas vieram você pode começar a entender pra onde elas vão evoluir no futuro. O que acharam desse episódio? Se gostaram deixem um joinha, assinem o canal e não deixem de clicar no sininho pra não perder a parte 2 dessa história. A gente se vê, até mais!</p>
]]></content:encoded><category>linguagens-de-programacao</category><category>ciencia-da-computacao</category><category>programacao</category><category>akitando</category></item><item><title>[Manga-Downloadr] Portando de Crystal para Ruby (e um pouco de JRuby)</title><link>https://akitaonrails.github.io/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby/</guid><pubDate>Mon, 06 Jun 2016 18:06:00 -0300</pubDate><description>&lt;p&gt;Eu tenho um projetinho pessoal antigo chamado &lt;a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener"&gt;Manga Downloadr&lt;/a&gt; que publiquei em 2014. Era uma versão bem tosca. Eu estava experimentando com requisições assíncronas usando Typhoeus e no final o código acabou ficando uma bagunça absurda.&lt;/p&gt;
&lt;p&gt;A natureza do Manga Downloader original é a de um web crawler: ele baixa páginas HTML, faz parse para encontrar coleções de links e segue baixando até que um conjunto de imagens seja salvo. Depois eu organizo tudo em volumes, otimizo para caber na resolução de tela do Kindle e compilo em arquivos PDF. Isso faz desse projeto um exercício interessante para tentar fazer requisições HTTP concorrentes e processar os resultados.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu tenho um projetinho pessoal antigo chamado <a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener">Manga Downloadr</a> que publiquei em 2014. Era uma versão bem tosca. Eu estava experimentando com requisições assíncronas usando Typhoeus e no final o código acabou ficando uma bagunça absurda.</p>
<p>A natureza do Manga Downloader original é a de um web crawler: ele baixa páginas HTML, faz parse para encontrar coleções de links e segue baixando até que um conjunto de imagens seja salvo. Depois eu organizo tudo em volumes, otimizo para caber na resolução de tela do Kindle e compilo em arquivos PDF. Isso faz desse projeto um exercício interessante para tentar fazer requisições HTTP concorrentes e processar os resultados.</p>
<p>Um ano depois eu estava aprendendo Elixir. O Manga Downloadr era um bom candidato para eu descobrir como implementar a mesma coisa em outra linguagem. Você pode acompanhar o meu processo de aprendizado <a href="http://www.akitaonrails.com/exmangadownloadr"target="_blank" rel="noopener">nessa série de posts</a>.</p>
<p>Finalmente, venho aprendendo mais sobre Crystal, uma plataforma inspirada em Ruby que compila código fonte parecido com Ruby em binários nativos otimizados via LLVM. E de bônus oferece canais e fibras no estilo CSP, parecido com Go, para permitir código concorrente.</p>
<p>Então adaptei minha versão Elixir para Crystal e o resultado é esse código que você encontra no Github como <a href="https://github.com/akitaonrails/cr_manga_downloadr"target="_blank" rel="noopener">akitaonrails/cr_manga_downloadr</a>.</p>
<p>Funciona muito bem e roda bem rápido, limitado principalmente por quantas requisições o MangaReader consegue responder concorrentemente e pela velocidade/confiabilidade da conexão de Internet. E como minha versão Ruby original era um código terrível, era uma boa hora para reescrevê-la. E como Crystal é surpreendentemente próximo de Ruby, decidi portar.</p>
<blockquote>
  <p>A portagem foi quase trivial demais</p>

</blockquote>
<p>Foi praticamente copiar e colar o código Crystal sem as anotações de tipo. E tive que substituir a implementação leve de Fibers e Channel para concorrência pelas tradicionais Threads de Ruby.</p>
<p>A nova versão 2.0 da ferramenta em Ruby pode ser encontrada nesse repositório no Github: <a href="https://github.com/akitaonrails/manga-downloadr"target="_blank" rel="noopener">akitaonrails/manga-downloadr</a>.</p>
<h3>Ruby, Ruby por todo lado!<span class="hx:absolute hx:-mt-20" id="ruby-ruby-por-todo-lado"></span>
    <a href="#ruby-ruby-por-todo-lado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Transitar entre Ruby e Crystal não é tão difícil. O time do Crystal fez um trabalho fantástico implementando uma Standard Library (stdlib) muito sólida, que se assemelha bastante à do MRI Ruby.</p>
<p>Por exemplo, vamos comparar primeiro um trecho da minha versão Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//img[contains(@id, &#39;img&#39;)]&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">as</span><span class="p">(</span><span class="no">XML</span><span class="o">::</span><span class="no">NodeSet</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora vamos olhar a versão portada para Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s1">&#39;#img&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É impressionante como são parecidos, até mesmo nas chamadas de stdlib como <code>URI.parse</code> ou métodos de Array como <code>split</code>.</p>
<blockquote>
  <p>Quando você remove as anotações de tipo da versão Crystal, é 99% Ruby.</p>

</blockquote>
<p>Ruby pouco se importa se você está chamando um método em um objeto Nil - em tempo de compilação. Crystal faz checagens em tempo de compilação e, se ele acha que a chamada de método pode acontecer sobre Nil, simplesmente não compila. Isso é uma grande vitória para evitar bugs sutis.</p>
<p>Em Rails estamos acostumados com o famigerado método <code>#try</code>. O Ruby 2.3 introduziu o <a href="http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/"target="_blank" rel="noopener">safe navigation operator</a>.</p>
<p>Então, em Ruby 2.3 com Rails, ambas as linhas a seguir são válidas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">&amp;.</span><span class="n">something</span><span class="o">&amp;.</span><span class="n">something2</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Em Crystal podemos fazer assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something2</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, é parecido. Use com cuidado.</p>
<p>Como mencionei antes, Crystal é próximo de Ruby, mas a intenção dele é ser uma linguagem própria, então não dá para simplesmente carregar Rubygems sem portar. Nesse exemplo eu não tenho Nokogiri para fazer parse de HTML. E é justamente aí que a stdlib brilha: Crystal vem com parsers de XML/HTML e JSON suficientemente bons. Então podemos fazer parse do HTML como XML e usar XPath puro.</p>
<p>No lugar do <code>Net::HTTP</code> do Ruby temos <code>HTTP::Client</code> (mas seus métodos e semântica são surpreendentemente similares).</p>
<p>Existem outras diferenças, por exemplo, esse é o arquivo principal que faz <code>require</code> de todos os outros em Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="vg">$LOAD_PATH</span><span class="o">.</span><span class="n">unshift</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s2">&#34;lib&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/records.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/downloadr_client.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/concurrency.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/chapters.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/pages.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/page_image.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/image_downloader.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/workflow.rb&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E essa é a versão Crystal do mesmo manifesto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./cr_manga_downloadr/*&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Por outro lado, precisamos ser um pouco mais explícitos em cada arquivo de código fonte Crystal e declarar as dependências específicas onde forem necessárias. Por exemplo, no arquivo <code>pages.cr</code> ele começa assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./downloadr_client&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;xml&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Crystal tem menos espaço para &ldquo;mágica&rdquo;, mas ainda assim consegue manter um alto nível de abstração.</p>
<h3>Uma palavra sobre Tipos<span class="hx:absolute hx:-mt-20" id="uma-palavra-sobre-tipos"></span>
    <a href="#uma-palavra-sobre-tipos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p>Podemos passar a próxima década masturbando sobre tudo que existe sobre Tipos, e vai ser extremamente chato.</p>

</blockquote>
<p>A única coisa que você precisa entender: o compilador precisa conhecer a assinatura dos métodos das classes antes de poder usá-las. Não existe um componente em Runtime que possa introspectar objetos em tempo real, como em Ruby e em outras linguagens dinâmicas (até Objective-C/Swift consegue fazer mais coisa dinâmica do que Crystal).</p>
<p>Na maior parte do tempo o compilador do Crystal é inteligente o bastante para inferir os tipos para você, então você não precisa ser absolutamente explícito. Você deve seguir o que o compilador indica para saber quando usar Type Annotations.</p>
<p>O que pode te assustar de início é a necessidade de Type Annotations, entender Generics e por aí vai. O compilador vai cuspir cada dump de erro assustador, e você vai precisar se acostumar. A maioria dos erros assustadores costuma ser ou uma Type Annotation faltando, ou você tentando chamar um método em um possível objeto Nil.</p>
<p>Por exemplo, se eu mudar a linha a seguir no arquivo de teste <code>page_image_spec.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># linha 8:</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">PageImage</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;www.mangareader.net&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="s2">&#34;/naruto/662/2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># linha 10:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># image.try(&amp;.host).should eq(&#34;i8.mangareader.net&#34;)</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span><span class="o">.</span><span class="n">host</span><span class="o">.</span><span class="n">should</span> <span class="n">eq</span><span class="p">(</span><span class="s2">&#34;i8.mangareader.net&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A linha comentada reconhece que a instância <code>image</code> pode vir como Nil, então adicionamos uma chamada explícita de <code>#try</code> no spec.</p>
<p>Se tentarmos compilar sem esse reconhecimento, esse é o erro que o compilador vai despejar em você:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec                                                        [
Error in ./spec/cr_manga_downloadr/page_image_spec.cr:10: undefined method &#39;host&#39; for Nil (compile-time type is CrMangaDownloadr::Image?)

    image.host.should eq(&#34;i8.mangareader.net&#34;)
          ^~~~

=============================================================================

Nil trace:

  ./spec/cr_manga_downloadr/page_image_spec.cr:8

        image = CrMangaDownloadr::PageImage.new(&#34;www.mangareader.net&#34;).fetch(&#34;/naruto/662/2&#34;)
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tem um stacktrace gigante depois desse trecho, mas você só precisa prestar atenção nas primeiras linhas, que já dizem o que está errado: &ldquo;undefined method &lsquo;host&rsquo; for Nil (compile-time type is CrMangaDownloadr::Image?)&rdquo;. Se você sabe ler, na maior parte do tempo não vai ter problema nenhum.</p>
<p>Agora, <code>Chapters</code>, <code>Pages</code>, <code>PageImage</code> (todos subclasses de <code>DownloadrClient</code>) são basicamente a mesma coisa: fazem requisições com <code>HTTP::Client</code>.</p>
<p>É assim que a classe <code>Pages</code> é implementada:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">chapter_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="n">chapter_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath_nodes</span><span class="p">(</span><span class="s2">&#34;//div[@id=&#39;selectpage&#39;]//select[@id=&#39;pageMenu&#39;]//option&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">node</span><span class="o">|</span> <span class="o">[</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">text</span> <span class="n">as</span> <span class="nb">String</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>#get</code> é um método da superclasse <code>DownloadrClient</code> que recebe uma String <code>chapter_link</code> e um bloco. O bloco recebe uma coleção de nós <code>html</code> já parseada e podemos brincar com ela, retornando um Array de Strings.</p>
<p>É por isso que temos o <code>(Array(String))</code> ao herdar de <code>DownloadrClient</code>. Vejamos como a superclasse <code>DownloadrClient</code> é implementada.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">DownloadrClient</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="n">uri</span> <span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="no">XML</span><span class="o">::</span><span class="no">Node</span> <span class="o">-&gt;</span> <span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">response</span> <span class="o">=</span> <span class="vi">@http_client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">301</span>
</span></span><span class="line"><span class="cl">        <span class="n">get</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Location&#34;</span><span class="o">]</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">        <span class="n">parsed</span> <span class="o">=</span> <span class="no">XML</span><span class="o">.</span><span class="n">parse_html</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">parsed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você pode ver que essa classe recebe um Generic Type e usa ele como o tipo de retorno do bloco yieldado no método <code>#get</code>. O <code>XML::Node -&gt; T</code> é a declaração da assinatura do bloco, enviando <code>XML::Node</code> e recebendo aquilo que <code>T</code> for. Em tempo de compilação, imagine esse <code>T</code> sendo substituído por <code>Array(String)</code>. É assim que dá para criar classes capazes de lidar com qualquer número de Tipos diferentes sem precisar fazer overload para polimorfismo.</p>
<p>Se você vem de Java, C#, Go ou qualquer outra linguagem moderna estaticamente tipada, provavelmente já sabe o que é um Generic.</p>
<p>Você pode ir bem longe com Generics, veja como começa o nosso <code>Concurrency.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                  <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E é assim que usamos no <code>workflow.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="kp">private</span> <span class="k">def</span> <span class="nf">fetch_pages</span><span class="p">(</span><span class="n">chapters</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="sc">?)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;Fetching pages from all chapters ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">Concurrency</span><span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">,</span> <span class="no">Pages</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">chapters</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">engine</span><span class="o">.</span><span class="n">try</span><span class="p">(</span> <span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nesse exemplo, imagine <code>A</code> sendo substituído por <code>String</code>, <code>B</code> também sendo substituído por <code>String</code> e <code>C</code> sendo substituído por <code>Pages</code> na classe <code>Concurrency</code>.</p>
<p>Essa é a &ldquo;primeira-versão-que-funcionou&rdquo;, então provavelmente está pouco idiomática. Talvez dê para resolver com menos exercício de Generics ou, quem sabe, eu pudesse simplificar com o uso de Macros. Mas está funcionando bem assim.</p>
<p>A versão pura em Ruby acaba ficando assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine_klass</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">config</span> <span class="o">=</span> <span class="no">Config</span><span class="o">.</span><span class="n">new</span><span class="p">,</span> <span class="n">turn_on_engine</span> <span class="o">=</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Essa versão é muito &ldquo;mais simples&rdquo; em termos de densidade de código fonte. Em compensação, teríamos que testar essa versão Ruby muito mais, porque ela tem várias permutações diferentes (a gente até injeta classes via <code>engine_klass</code>) e precisamos garantir que tudo responda corretamente. Na prática, deveríamos adicionar testes para todas as combinações dos argumentos do inicializador.</p>
<p>Na versão Crystal, como todos os tipos foram checados em tempo de compilação, foi mais exigente em termos de anotações; mas podemos ter bastante certeza de que se compilou, vai rodar como esperado.</p>
<blockquote>
  <p>Não estou dizendo que Crystal dispensa specs.</p>

</blockquote>
<p>Compiladores só vão até certo ponto. Mas até onde é &ldquo;certo ponto&rdquo;? Sempre que você é &ldquo;forçado&rdquo; a adicionar Type Annotations, eu afirmo que essas partes ou estão tentando ser espertas demais, ou são intrinsecamente complexas. Essas são as partes que exigiriam camadas extras de testes em Ruby e, se conseguirmos adicionar as anotações direito, podemos ter menos testes (não precisamos cobrir a maior parte das permutações) na versão Crystal (uma complexidade exponencial de permutações poderia cair para uma complexidade linear, eu acho).</p>
<h3>Uma palavra sobre Threads em Ruby<span class="hx:absolute hx:-mt-20" id="uma-palavra-sobre-threads-em-ruby"></span>
    <a href="#uma-palavra-sobre-threads-em-ruby" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Os principais conceitos que você precisa entender sobre concorrência em Ruby são esses:</p>
<ul>
<li>MRI Ruby tem um GIL, um Global Interpreter Lock, que proíbe código de rodar concorrentemente.</li>
<li>MRI Ruby tem acesso e expõe Threads nativas desde a versão 1.9. Mas mesmo se você disparar várias Threads, elas vão rodar sequencialmente, porque só uma thread pode segurar o Lock por vez.</li>
<li>Operações de I/O são a exceção: elas liberam o Lock para que outras threads rodem enquanto a operação está esperando completar. O SO sinaliza o programa via poll de nível de sistema.</li>
</ul>
<p>Isso significa que se sua aplicação é <strong>I/O intensiva</strong> (requisições HTTP, leituras ou escritas em arquivos, operações de socket etc), você terá <em>alguma</em> concorrência. Um servidor web, como Puma por exemplo, pode tirar algum proveito de Threads, porque uma boa parte das operações envolve receber requisições HTTP e mandar respostas HTTP pela rede, o que deixa o processo Ruby ocioso enquanto espera.</p>
<p>Se sua aplicação é CPU intensiva (algoritmos pesados, processamento de dados, coisas que realmente esquentam o CPU), aí Threads nativas não ajudam, só uma roda por vez. Se você tem múltiplos cores no seu CPU, pode dar Fork no seu processo para o número de cores disponíveis.</p>
<p>Dê uma olhada em <a href="https://github.com/grosser/parallel"target="_blank" rel="noopener">grosser/parallel</a> para facilitar.</p>
<p>É por isso que Puma também tem um modo &ldquo;worker&rdquo;. &ldquo;Worker&rdquo; é o nome que normalmente damos para processos filhos forkados.</p>
<p>No caso desse processo de download, ele vai fazer milhares de requisições HTTP para raspar os metadados necessários das páginas do MangaReader. Então é muito mais I/O intensivo do que CPU intensivo (as partes CPU intensivas são o parsing do HTML e, mais tarde, o redimensionamento das imagens e a compilação do PDF).</p>
<p>Uma versão sequencial do que precisa ser feito, em Ruby, fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_sequential</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch_results</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">batch_results</span> <span class="o">||</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se temos 10.000 links na <code>collection</code>, primeiro fatiamos no tamanho que <code>@config.download_batch_size</code> define e iteramos sobre essas fatias menores, chamando algum bloco e acumulando os resultados. Esse é um algoritmo ingênuo, como você vai descobrir na próxima seção, mas vamos seguir.</p>
<p>Em Elixir você pode disparar micro-processos para fazer as requisições HTTP em paralelo. Em Crystal você pode disparar Fibers e esperar as requisições HTTP completarem e sinalizar os resultados via Channels.</p>
<p>Os dois são leves e você pode ter centenas ou até milhares rodando em paralelo. O Manga Reader provavelmente vai reclamar se você mandar tudo isso de uma vez, então o limite não está no código, e sim no serviço externo.</p>
<p>Para transformar a versão sequencial em uma concorrente, é isso que podemos fazer em Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">spawn</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                    <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">reply</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">reply</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">reply</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">reply</span>
</span></span><span class="line"><span class="cl">        <span class="n">results</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span><span class="n">reply</span><span class="o">.</span><span class="n">flatten</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pegar uma coleção enorme e fatiar em &lsquo;batches&rsquo; menores é fácil. Agora temos uma coleção <code>batch</code> menor. Para cada item (geralmente uma URI) damos <code>spawn</code> em uma Fiber e chamamos um bloco que vai requisitar e processar os resultados. Quando termina o processamento, envia os resultados por um <code>channel</code>.</p>
<p>Quando terminamos de iterar sobre o batch e disparar todas essas Fibers, podemos &ldquo;esperar&rdquo; por elas chamando <code>channel.receive</code>, que vai começar a receber os resultados conforme as Fibers vão terminando de requisitar/processar cada URI.</p>
<p>Acumulamos os resultados e seguimos para o próximo batch da coleção até terminar. A quantidade de concorrência é determinada pelo tamanho do batch (é parecido com <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate/"target="_blank" rel="noopener">o que fiz com &lsquo;poolboy&rsquo; em Elixir</a> onde iniciamos um número fixo de processos rodando em paralelo para evitar fazer um Denial of Service no Manga Reader).</p>
<p>A propósito, essa implementação em Crystal é parecida com o que você faria em Go usando Channels.</p>
<p>Na versão Ruby você pode disparar Threads nativas - que têm bastante overhead para spawnar! - e assumir que as requisições HTTP vão rodar quase todas em paralelo. Como é I/O intensivo, dá para ter todas em paralelo. Fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="no">Thread</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">        <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">        <span class="n">mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="o">[]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:join</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As Threads são todas inicializadas em estado &ldquo;pausado&rdquo;. Depois de instanciar todas essas Threads, podemos chamar <code>#join</code> em cada uma e esperar todas terminarem.</p>
<p>Quando cada Thread termina o mesmo processo de requisição/processamento de URI, os resultados precisam ser acumulados em um armazenamento global, nesse caso um simples array chamado <code>results</code>. Mas como existe a chance de duas ou mais threads tentarem atualizar o mesmo array, a gente sincroniza o acesso (não tenho certeza se o acesso a Array em Ruby já é sincronizado, mas acho que não). Para sincronizar o acesso usamos um Mutex, que é um lock fino, garantindo que só 1 thread possa modificar o array global por vez.</p>
<p>Para provar que Ruby suporta operações de I/O concorrentes, adicionei 2 métodos na classe <code>Concurrent</code>, o primeiro é só <code>#fetch</code> e é a implementação com Threads acima. O segundo se chama <code>#fetch_sequential</code> e é a versão sequencial mostrada no início dessa seção. E adicionei o seguinte spec:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">it</span> <span class="s2">&#34;should check that the fetch implementation runs in less time than the sequential version&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Concurrency</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Pages</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;/onepunch-man/96&#34;</span><span class="o">]</span> <span class="o">*</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">  <span class="no">WebMock</span><span class="o">.</span><span class="n">allow_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">concurrent_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">sequential_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:fetch_sequential</span><span class="p">,</span> <span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="sr">/\((.*?)\)$/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">concurrent_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">cm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="sr">/\((.*?)\)/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">sequential_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">sm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># esperado que a versao concorrente seja proxima de 10 vezes mais rapida que a sequencial</span>
</span></span><span class="line"><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">sm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">be</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="n">cm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span> <span class="o">*</span> <span class="mi">9</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="no">WebMock</span><span class="o">.</span><span class="n">disable_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como ele usa WebMock, eu primeiro desabilito durante esse spec. Crio uma coleção falsa de 10 links reais para o MangaReader. E aí faço benchmark da versão concorrente baseada em Threads e da versão sequencial pura. Como temos 10 links e são todos iguais, dá para assumir que a versão sequencial vai ser quase <strong>10 vezes mais lenta</strong> que a versão baseada em Threads. E é exatamente isso que esse spec compara e prova (o spec falha se a versão concorrente não for pelo menos 9x mais rápida).</p>
<p>Para comparar todas as versões dos Manga Downloadrs, deixei baixar e compilar uma coleção inteira de mangá, nesse caso &ldquo;One-Punch Man&rdquo;, que tem quase <strong>1.900 páginas/imagens</strong>. Estou medindo só os processos de fetching e scraping, pulando o download das imagens em si, redimensionamento e geração do PDF, porque eles tomam a maior parte do tempo e o redimensionamento e a parte do PDF são feitos pelo <code>mogrify</code> e <code>convert</code> do ImageMagick.</p>
<p>Esse é o tempo que essa nova versão Ruby leva para baixar e raspar quase 1.900 páginas (usando MRI Ruby 2.3.1):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>12,42s user 1,33s system 23% cpu 57,675 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse é o tempo que a versão Crystal leva:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>4,03s user 0,40s system 7% cpu 59,207 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Só por diversão tentei rodar a versão Ruby sob JRuby 9.1.1.0. Para rodar com JRuby basta adicionar a linha a seguir no <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">ruby</span> <span class="s2">&#34;2.3.0&#34;</span><span class="p">,</span> <span class="ss">:engine</span> <span class="o">=&gt;</span> <span class="s1">&#39;jruby&#39;</span><span class="p">,</span> <span class="ss">:engine_version</span> <span class="o">=&gt;</span> <span class="s1">&#39;9.1.1.0&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bundle install, roda normalmente, e esse é o resultado:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>47,80s user 1,99s system 108% cpu 45,967 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E esse é o tempo que a versão Elixir leva:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>11,38s user 1,04s system 85% cpu 14,590 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Reality Check!<span class="hx:absolute hx:-mt-20" id="reality-check"></span>
    <a href="#reality-check" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se você só olhar os tempos acima, pode chegar a conclusões erradas.</p>
<p>Antes de mais nada, é uma <strong>comparação injusta</strong>. A versão Elixir usa um algoritmo bem diferente das versões Ruby e Crystal.</p>
<p>Em Elixir eu subo um pool de processos, mais ou menos 50 deles. Aí disparo 50 requisições HTTP de uma vez. Quando cada processo termina, ele se libera de volta para o pool e eu posso disparar outra requisição HTTP da fila de links. Então é um <strong>fluxo constante</strong> de no máximo 50 requisições HTTP, constantemente.</p>
<p>As versões Crystal e Ruby/JRuby fatiam os 1.900 links em batches de 40 links e aí eu disparo 40 requisições de uma vez. Essa implementação espera todas as 40 terminarem para disparar mais 40. Ou seja, nunca é um fluxo constante, são rajadas de 40 requisições. Cada batch é freado pela requisição mais lenta do batch, sem dar chance para as outras requisições passarem.</p>
<p>É uma diferença de arquitetura. Elixir torna muito mais fácil fazer streams e Crystal, com seu estilo CSP, torna mais fácil fazer rajadas. Uma abordagem melhor seria enfileirar os 1.900 links e usar algo como Sidekiq.cr para ir consumindo um link por vez (spawnando 40 fibers para servir como um &ldquo;pool&rdquo;, por exemplo).</p>
<p>A versão Elixir tem uma arquitetura mais eficiente, e é por isso que ela leva no máximo 15 segundos para baixar todos os links de imagens, enquanto a versão Crystal leva quase um minuto inteiro para terminar (a soma das requisições mais lentas em cada batch).</p>
<p>Agora, você vai se surpreender ao ver que a versão Crystal é, na verdade, um pouco mais lenta que a versão Ruby! E você não vai ficar tão surpreso ao ver o JRuby mais rápido aos 45 segundos!</p>
<p>Essa é mais uma evidência de que você não deve descartar Ruby (e que deveria experimentar JRuby com mais frequência). Como expliquei antes, ele suporta concorrência em operações de I/O e as aplicações que testei são todas pesadas em I/O.</p>
<p>A diferença provavelmente está na maturidade da biblioteca <code>Net::HTTP</code> do Ruby contra a <code>HTTP::Client</code> do Crystal. Tentei vários ajustes na versão Crystal, mas não consegui ficar muito mais rápido. Tentei batches maiores, mas por algum motivo a aplicação simplesmente trava, pausa, e nunca libera. Tenho que dar Ctrl-C e tentar de novo até finalmente passar. Se alguém souber o que estou fazendo de errado, não se esqueça de escrever na seção de comentários abaixo.</p>
<p>Parte disso provavelmente é por conta dos servidores instáveis do MangaReader, eles devem ter algum tipo de prevenção contra DoS, throttling de conexões ou algo do tipo.</p>
<p>De qualquer forma, quando passam, como o algoritmo do Ruby e do Crystal são essencialmente o mesmo, eles levam aproximadamente o mesmo tempo para completar. O que falta é eu evoluir esse algoritmo para usar algo como Sidekiq ou implementar um esquema interno de fila/pool de workers.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O objetivo desse experimento era aprender mais sobre as capacidades do Crystal e o quão fácil seria ir e voltar com Ruby.</p>
<p>Como você pode ver, existem muitas diferenças, mas não é tão difícil. Posso estar deixando algo passar, mas esbarrei em algumas dificuldades quando empurrei <code>HTTP::Client</code> + Fibers para o limite, como expliquei acima. Se você sabe o que estou fazendo de errado, me avise.</p>
<p>A diferença entre os algoritmos do Elixir e do Ruby/Crystal mostra mais do que diferenças de performance entre linguagens, mostra também a importância da arquitetura e dos algoritmos na performance geral. Esse teste foi pouco conclusivo, apenas um indício de que minha implementação ingênua de Fibers precisa de mais trabalho, e que a forma natural do Elixir lidar com processos paralelos facilita atingir níveis mais altos de paralelismo.</p>
<p>Isso serve como um aperitivo do que Crystal pode fazer, e também de que Ruby continua no jogo. E também de que Elixir certamente é algo para se acompanhar de perto.</p>
]]></content:encoded><category>anime-e-manga</category><category>linguagens-de-programacao</category><category>ruby</category></item><item><title>Flertando com Crystal, a Perspectiva de um Rubyista</title><link>https://akitaonrails.github.io/2016/05/31/flertando-com-crystal-perspectiva-de-um-rubyista/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/05/31/flertando-com-crystal-perspectiva-de-um-rubyista/</guid><pubDate>Tue, 31 May 2016 18:21:00 -0300</pubDate><description>&lt;p&gt;Quem me acompanha sabe que ando me distraindo com &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt; ultimamente. Vou fazer uma introdução grande antes de mergulhar no Crystal, então tenham paciência.&lt;/p&gt;
&lt;p&gt;Vou logo dizer: o Erlang OTP e suas entranhas me intrigam mais do que apenas a novidade da sintaxe da linguagem. Acredito que se você quer construir sistemas altamente distribuídos e também altamente confiáveis, ou você vai usar Erlang (via Elixir), ou vai acabar replicando boa parte do que o Erlang já tem com OTP.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Quem me acompanha sabe que ando me distraindo com <a href="/elixir">Elixir</a> ultimamente. Vou fazer uma introdução grande antes de mergulhar no Crystal, então tenham paciência.</p>
<p>Vou logo dizer: o Erlang OTP e suas entranhas me intrigam mais do que apenas a novidade da sintaxe da linguagem. Acredito que se você quer construir sistemas altamente distribuídos e também altamente confiáveis, ou você vai usar Erlang (via Elixir), ou vai acabar replicando boa parte do que o Erlang já tem com OTP.</p>
<p>Isso fica claro quando você olha para os vários sistemas distribuídos baseados em Java/Scala como Hadoop, Spark, Cassandra, e percebe que todos eles teriam se beneficiado da arquitetura OTP do Erlang. Por outro lado, se você acompanha o Cassandra, vai ver que mesmo o Java ainda sofre para competir com C++, basta comparar com o clone chamado <a href="http://www.scylladb.com/"target="_blank" rel="noopener">ScyllaDB</a>.</p>
<p>Acredito que Elixir (talvez com HiPE) consegue competir na mesma liga do Java em sistemas distribuídos, evitando que as pessoas abandonem a plataforma Erlang em favor de C ou Java como <a href="https://www.infoq.com/news/2012/01/Katz-CouchDB-Couchbase-Server"target="_blank" rel="noopener">aconteceu com o CouchDB</a> lá em 2012, por falta de interesse e pela sintaxe exótica. E como o Java já tem um ecossistema gigante, Clojure entrou no jogo principalmente porque consegue interfacear diretamente com ele.</p>
<p>Vejo Go também como concorrente do Java, mas para outros casos de uso, particularmente no espaço de ferramentas de sistema onde você tem muito C/C++ e código de cola em Perl e Python. Um exemplo óbvio é a orquestração de Docker. Sim, dá pra fazer microsserviços, crawlers e outras coisas, mas eu não estou tão animado em construir grandes &ldquo;aplicações&rdquo; nele, embora nada impeça. De novo, é só opinião pessoal.</p>
<p>Rust é um forte concorrente para desenvolvimento de sistemas de baixo nível. Acho que ele pode substituir C em muitos casos de uso e ser usado para implementar bibliotecas e componentes a serem consumidos por outras linguagens, com o benefício adicional de ser uma linguagem moderna que tenta evitar problemas de segurança como o recente <a href="https://tonyarcieri.com/would-rust-have-prevented-heartbleed-another-look"target="_blank" rel="noopener">fiasco do Heartbleed</a>. Mas por causa do &ldquo;Borrow system&rdquo; eu acho ele extremamente burocrático, especialmente vindo de linguagens altamente dinâmicas como Ruby, Python ou mesmo Swift ou Elixir. Não estou dizendo que isso seja ruim, é só algo que leva muito mais tempo para ficar confortável do que eu esperava.</p>
<p>Crystal é algo entre Rust e Go. LLVM é algo que você precisa ter no radar, e por causa do <a href="http://appleinsider.com/articles/08/06/20/apples_other_open_secret_the_llvm_complier"target="_blank" rel="noopener">apoio da Apple</a> ao Swift está melhor do que nunca. Crystal é <em>similar</em> ao RubyMotion no sentido de que ambos são parecidos com Ruby mas não totalmente compatíveis, e ambos são front-end parsers para o LLVM. RubyMotion, por outro lado, está mais próximo do Ruby e até consegue usar algumas bibliotecas Ruby quase sem mudanças.</p>
<p>Você tem que tirar o chapéu para o Ary Borenszweig e seus contribuidores. É muito impressionante o quanto eles avançaram em tão pouco tempo, sem ter os bolsos fundos da Mozilla, Apple ou Google.</p>
<h3>Limitações<span class="hx:absolute hx:-mt-20" id="limitações"></span>
    <a href="#limita%c3%a7%c3%b5es" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal toma bastante emprestado do Ruby, mas não é objetivo dele atingir qualquer nível de compatibilidade. Ele é uma linguagem fortemente e estaticamente tipada com Inferência de Tipos. Não tem nenhum componente de runtime como RubyMotion ou Swift, então não tem qualquer noção de introspecção ou reflexão sobre objetos.</p>
<p>Resumindo: você não tem &ldquo;#eval&rdquo; nem &ldquo;#send&rdquo;. A falta de avaliação em runtime acaba não sendo tão grave porque você tem manipulação de AST em tempo de compilação através de um sistema bom o bastante de Macros (mais sobre isso adiante).</p>
<p>Mas a falta de &ldquo;#send&rdquo; dói um pouco. É a única coisa que afasta o Crystal da flexibilidade dinâmica dos objetos do Ruby. Mas dá para entender por que isso não é prioridade.</p>
<p>Na versão 0.17.0, Crystal tem uma grande limitação: ele usa um <a href="http://crystal-lang.org/2013/12/05/garbage-collector.html"target="_blank" rel="noopener">garbage collector conservador Boehm-Demers-Weiser</a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/asterite">@asterite</a> <a href="https://twitter.com/headius">@headius</a> I assumed as much... the Boehm GC, although handy for starting a new language, will only get you so far</p>&mdash; Jason Frey (@Fryguy9) <a href="https://twitter.com/Fryguy9/status/736990644082757632">May 29, 2016</a></blockquote>
<p>A linguagem está atualmente implementada como um processo single-threaded. Isso significa que você <em>provavelmente</em> não consegue usar 100% de todas as CPUs da sua máquina com um único processo. Posso estar enganado aqui, é só a minha primeira impressão.</p>
<p>Charles Nutter (do JRuby) faz um alerta sobre isso:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Fryguy9">@fryguy9</a> From their site: <a href="https://t.co/uOjIT0c8ji">https://t.co/uOjIT0c8ji</a> It does say something about "when" parallelism happens. Risky to start single-threaded.</p>&mdash; Charles Nutter (@headius) <a href="https://twitter.com/headius/status/736982955239870464">May 29, 2016</a></blockquote>
<p>É uma faca de dois gumes. Usar um GC &ldquo;genérico&rdquo; plug-and-play como o Boehm - o que em si não é ruim, mas talvez não seja nem de longe tão poderoso quanto o conjunto de GCs de alta performance da própria JVM, como o novíssimo G1GC.</p>
<p>Fazer com que a linguagem seja single-threaded por padrão também significa que toda a biblioteca padrão e todo o ecossistema foi construído assumindo que não há paralelismo. Então não há race-conditions e, consequentemente, nenhum uso de sincronização adequada em lugar nenhum. Se um dia eles decidirem adicionar multi-threading, todo o código existente vai estar inseguro para threads. Provavelmente é isso que o Charles está alertando.</p>
<p>Também acho que não é um problema urgente. Você provavelmente não vai construir um sistema altamente paralelo com Crystal, mas talvez esse não seja o caso de uso. Para isso, você deveria estar usando Elixir + OTP, Scala + Akka, por exemplo.</p>
<h3>Concorrência<span class="hx:absolute hx:-mt-20" id="concorrência"></span>
    <a href="#concorr%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Não ter acesso a Threads nativas não significa que você fica sem Concorrência. <a href="https://blog.golang.org/concurrency-is-not-parallelism"target="_blank" rel="noopener">Concorrência não é Paralelismo</a>. Desde a <a href="http://crystal-lang.org/2015/04/30/crystal-0.7.0-released.html"target="_blank" rel="noopener">versão 0.7.0</a> eles adicionaram suporte para I/O não-bloqueante e também Fibers e Channels.</p>
<p>Em poucas palavras, uma Fiber é um tipo especial de corotina. É um &ldquo;pedaço de execução&rdquo; que pode &ldquo;pausar&rdquo; a si mesmo e devolver o controle de execução para quem o chamou. Ele pode pausar a si mesmo, mas não pode ser pausado por fora como o Scheduler do Erlang consegue pausar seus processos em execução, por exemplo.</p>
<p>Nesse caso temos uma espécie de &ldquo;Fiber Scheduler&rdquo; que consegue &ldquo;retomar&rdquo; outras Fibers. Ele é, claro, atualmente single-threaded. E funciona como um Event Loop. Sempre que você tem uma operação de I/O não-bloqueante esperando leitura de arquivo, envio de pacote de rede, esse tipo de coisa, ele consegue devolver o controle para outras fibers retomarem o trabalho.</p>
<p>Isso faz com que ele funcione mais ou menos como o Node.js. Javascript também é uma linguagem single-threaded e o Node.js é basicamente uma implementação de event loop. Funções declaram outras funções anônimas como callbacks e tudo funciona com base em triggers de I/O para chamar de volta essas funções a cada tick do reactor.</p>
<p>Em cima disso você pode adicionar <a href="https://github.com/crystal-lang/crystal/issues/1967"target="_blank" rel="noopener">&ldquo;Channels&rdquo;</a> como popularizados pela linguagem Go do Google. Você pode iniciar quantos channels quiser. Aí você pode dar spawn em Fibers no Scheduler. As Fibers podem executar e ficar enviando mensagens pelos Channels. O controle de execução é cedido para quem estiver esperando receber dos mesmos Channels. Quando uma delas recebe e executa, o controle volta para o Scheduler, permitindo que outras Fibers executem, e assim ficam &ldquo;pingando&rdquo; e &ldquo;pongando&rdquo;.</p>
<p>Falando em ping-pong, tem <a href="https://gobyexample.com/channel-directions"target="_blank" rel="noopener">este snippet no site &ldquo;Go by Example&rdquo;</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">pings</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;passed message&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&lt;-</span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E isso é basicamente a mesma coisa, mas implementada em Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">pings</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pong</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">message</span> <span class="o">=</span> <span class="n">pings</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">  <span class="n">pongs</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pings</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">pongs</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">ping</span> <span class="n">pings</span><span class="p">,</span> <span class="s2">&#34;passed message&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">pong</span> <span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">pongs</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;passed message&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mesma coisa, mas mais agradável aos meus olhos, opinião pessoal de novo - o que é esperado se você também é um Rubyista.</p>
<p>Então, Crystal tem um Event Loop estilo Node.js/Javascript na forma de um Fiber Scheduler e um mecanismo de Channel/CSP estilo Go para coordenar processamento concorrente. É um mecanismo de &ldquo;multitarefa cooperativa&rdquo;. Não é bom para processamento intensivo de CPU, como cálculos pesados ou processamento massivo de dados. Funciona melhor para processamento intensivo de I/O.</p>
<p>Se você tem uma Fiber fazendo processamento super pesado, ela vai bloquear o Scheduler, igual acontece no Node.js. A implementação atual do Scheduler está atrelada a uma única thread nativa, então por enquanto ela não consegue usar outras threads nativas disponíveis no sistema.</p>
<p>Como a implementação dos Channels é bem nova, ela ainda não é usada por toda a biblioteca padrão. Mas a implementação padrão do <code>HTTP::Server</code> usa Fibers. E como ele é compilado para um binário nativo, é <strong>muito mais rápido</strong> que Javascript, como mostra este <a href="https://github.com/sdogruyol/fast-http-server"target="_blank" rel="noopener">&ldquo;fast-http-server&rdquo;</a>:</p>
<ul>
<li>fast-http-server (Crystal)  18348.47rpm a 8.67ms de tempo médio de resposta</li>
<li>http-server (Node.js)   2105.55rpm a 47.92ms de tempo médio de resposta</li>
<li>SimpleHTTPServer (Python)   785.14rpm a 1.91ms de tempo médio de resposta</li>
</ul>
<p>Estamos falando de uma ordem de magnitude de 8 vezes mais rápido que Node.js e mais de 20 vezes mais rápido que Python.</p>
<p>Como sempre, você não deve confiar cegamente em <a href="https://github.com/kostya/benchmarks"target="_blank" rel="noopener">benchmarks sintéticos</a>, mas dependendo do tipo de processamento que você decida comparar, você verá Crystal bem no mesmo patamar de Rust, Go, Swift, D.</p>
<h3>Inferência de Tipos<span class="hx:absolute hx:-mt-20" id="inferência-de-tipos"></span>
    <a href="#infer%c3%aancia-de-tipos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal é uma linguagem estaticamente tipada. Mas ela não vai exigir que você declare cada tipo de antemão. Ela é bem inteligente na Inferência de Tipos, então se você só digitar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">b</span> <span class="o">=</span> <span class="s2">&#34;HELO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ela vai saber que &ldquo;a&rdquo; é um &ldquo;Int32&rdquo; e que &ldquo;b&rdquo; é uma &ldquo;String&rdquo;. A propósito, ao contrário do Ruby, todas as Strings precisam usar aspas duplas. Mas a coisa fica particularmente mais complicada quando você está lidando com estruturas de dados complexas como JSON.</p>
<p>Em Ruby eu posso simplesmente parsear uma String JSON e imediatamente explorar sua estrutura assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">first</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não dá para fazer isso em Crystal, ao invés a <a href="http://crystal-lang.org/api/JSON.html#mapping%28properties%2Cstrict%3Dfalse%29-macro"target="_blank" rel="noopener">abordagem recomendada</a> é declarar uma estrutura tipo schema assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="ss">ok</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Bool</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">files</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="no">FilesItemResponse</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesItemResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">filetype</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">created</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">timestamp</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">permalink</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_json = JSON.parse(response.body)[&#34;files&#34;].first[&#34;id&#34;]</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">FilesResponse</span><span class="o">.</span><span class="n">from_json</span><span class="p">(</span><span class="vi">@body</span><span class="p">)</span><span class="o">.</span><span class="n">files</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">id</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu sou muito acostumado com o duck typing do Ruby e a habilidade de consultar objetos em runtime. Em Crystal a coisa é diferente, e pode ficar bem cansativo mudar seu mindset para pensar em tipos de antemão. O compilador vai gritar muito durante seu ciclo de desenvolvimento até você se acostumar com esse conceito.</p>
<p>Fiz um experimento com parsing de JSON e o resultado é um projeto que chamei de <a href="https://github.com/akitaonrails/cr_slack_cleanup"target="_blank" rel="noopener">&ldquo;cr_slack_cleanup&rdquo;</a>, que mostra tanto essa ideia de Schemas JSON quanto Fibers e Channels como expliquei na seção anterior.</p>
<p>Update: depois que postei este artigo o @LuisLavena entrou em campo para me corrigir: dá para fazer como em Ruby, sem schemas:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/CrystalLanguage">@CrystalLanguage</a> re: JSON, you can parse it kinda-like Ruby, see JSON::Any: <a href="https://t.co/MD7PYy5AVH">https://t.co/MD7PYy5AVH</a></p>&mdash; Luis Lavena (@luislavena) <a href="https://twitter.com/luislavena/status/738005493189316608">June 1, 2016</a></blockquote>
<p>E fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;json&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="s">&lt;&lt;-JSON
</span></span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;files&#34;</span><span class="p">:</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;photo.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">99</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;another.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="n">item</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">].</span><span class="n">as_i</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, parsing de JSON pode ser quase idêntico ao que você faria em Ruby, embora ele também recomende Schemas porque acaba sendo um pouco mais rápido.</p>
<h3>Macros<span class="hx:absolute hx:-mt-20" id="macros"></span>
    <a href="#macros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A última feature importante que vale mencionar é a presença de Macros. Como você não tem controle do seu código em runtime, não tem &ldquo;eval&rdquo;, não tem &ldquo;send&rdquo;, então você ficaria sem como fazer metaprogramação de verdade.</p>
<p>Macros trazem de volta um pouco da metaprogramação do Ruby. Por exemplo, em Ruby, quando você inclui um Module em uma Class, existe o hook &ldquo;included?&rdquo; no Module onde você pode adicionar metaprogramação como coisas com &ldquo;class_eval&rdquo;.</p>
<p>Em Crystal, um Module também tem o hook &ldquo;included&rdquo; mas agora é uma Macro. Você pode fazer algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="n">included</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">if</span> <span class="vi">@type</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;Bar&#34;</span> <span class="s">%}
</span></span></span><span class="line"><span class="cl"><span class="s">    def self.hello
</span></span></span><span class="line"><span class="cl"><span class="s">      puts &#34;Bar&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    end
</span></span></span><span class="line"><span class="cl"><span class="s">    {% else %}</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">hello</span>
</span></span><span class="line"><span class="cl">      <span class="nb">puts</span> <span class="s2">&#34;HELLO&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">end</span> <span class="o">%</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Bar</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Something</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Bar</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;Bar&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Something</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;HELLO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É como ter templates &ldquo;ERB&rdquo; para código. Uma macro é código construindo código em tempo de compilação. Na AST resultante é como se você tivesse escrito o código repetitivo e chato na mão. O binário nativo compilado não se importa. Se você vem do C, é como pré-processamento, mas com controle sobre uma árvore AST de Nodes ao invés de só manipular código-fonte. Você até consegue fazer algo parecido com o venerado &ldquo;#method_missing&rdquo;.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="nb">method_missing</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{{</span><span class="n">pp</span> <span class="nb">name</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">hello_world</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;hello_world&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">bla_bla</span><span class="p">(</span><span class="s2">&#34;bla&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;bla_bla&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Qualquer macro pode receber uma AST como argumento e você pode manipular essa AST como bem entender. Por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>module Foo
  macro teste(ast)
    puts {{ast.stringify}}
  end
end

Foo.teste &#34;Hello World&#34;.split(&#34; &#34;).join(&#34; - &#34;)
# =&gt; (&#34;Hello World&#34;.split(&#34; &#34;)).join(&#34; - &#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nesse exemplo você só pega uma versão normalizada do código que passou como argumento. E o código não vai ser &ldquo;executado&rdquo; naquela posição.</p>
<p>Então, você pode definir novos métodos e chamar versões diferentes de métodos dependendo da combinação de elementos que você pega da AST. Um exemplo do que é possível é este experimento do desenvolvedor Oleksii que empacotei neste pequeno projeto que chamei de <a href="https://github.com/akitaonrails/cr_chainable_methods"target="_blank" rel="noopener">&ldquo;cr_chainable_methods&rdquo;</a>, e a gente consegue construir código bem diferente tanto de Crystal quanto de Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">pipe</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">split_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">append_message</span><span class="p">(</span><span class="s2">&#34;Bar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Bar</span><span class="o">.</span><span class="n">add_something</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">join</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Sim, um subconjunto do que um Pipe de verdade do Elixir faz.)</p>
<h3>Crystal Shards<span class="hx:absolute hx:-mt-20" id="crystal-shards"></span>
    <a href="#crystal-shards" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por fim, um aspecto que eu gosto no Crystal é que ele já tem gerenciamento de tarefas e gerenciamento de dependências resolvidos. Sem discussões sem fim sobre qual implementação de pacotes e dependências a gente &ldquo;deveria&rdquo; usar.</p>
<p>Você pode simplesmente escrever um script simples num arquivo como &ldquo;test.cr&rdquo; e rodar esse arquivo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal test.cr</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou você pode buildar e compilar para um binário nativo antes de executar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal build test.cr --release
./test</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E também dá para começar com uma estrutura de projeto adequada e ciclo de desenvolvimento assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal init app test
cd test
crystal build src/test.cr --release
./test
git add .
git commit -m &#34;initial commit&#34;
git push -u origin master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele monta uma estrutura de diretórios de projeto adequada, um repositório Git já vem pré-inicializado para você (por que diabos você programaria sem Git!?) com um arquivo &ldquo;.gitignore&rdquo; decente.</p>
<p>Você também vai encontrar um arquivo &ldquo;shard.yml&rdquo; onde você declara o nome da sua aplicação ou biblioteca, versão, autor, e também as suas dependências assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">kemal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.12.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">radix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">luislavena/radix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kilt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">jeromegn/kilt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">author</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Serdar Dogruyol &lt;dogruyolserdar@gmail.com&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse exemplo é do <a href="https://github.com/sdogruyol/kemal"target="_blank" rel="noopener">framework web Kemal</a> do Serdar. Ele depende das bibliotecas Radix e Kilt. Você precisa rodar <code>crystal deps</code> e ele vai buscar do Github antes de você poder compilar.</p>
<p>E mais: todo projeto vem com um diretório &ldquo;spec&rdquo; adequado onde você pode escrever testes no estilo Rspec e rodá-los usando <code>crystal spec</code>. E o resultado do runner de specs vai ser parecido com isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec
........................................................

Finished in 8.28 milliseconds
56 examples, 0 failures, 0 errors, 0 pending</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tudo embutido, sem precisar de bibliotecas externas.</p>
<p>E 8 milissegundos para rodar a suíte de testes inteira de um micro framework web? Nada mau. Se você somar o tempo de inicialização do executável, ainda dá em torno de 1 segundo de tempo total de execução. Bem impressionante.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal não tem como objetivo ser um substituto do Ruby como o JRuby. Então ele nunca vai rodar projetos Ruby existentes e complicados como Ruby on Rails ou mesmo Sinatra.</p>
<p>Para mim, Crystal preenche uma lacuna em alguns casos de uso onde eu usaria Rust ou Go. Ele tem o desempenho bruto de um binário nativo otimizado por LLVM, um sistema de concorrência razoavelmente rápido no estilo Go (e muito mais rápido e menos intensivo em recursos que Node.js), com o benefício adicional de uma sintaxe muito mais agradável estilo Ruby.</p>
<p>Você pode até começar a experimentar Crystal para construir extensões nativas Ruby sem precisar de FFI e sem ter que escrever C. Existe uma tentativa de <a href="https://github.com/startram/activesupport.cr"target="_blank" rel="noopener">port do ActiveSupport</a> como uma extensão nativa para o MRI Ruby, escrita como prova de conceito em Crystal.</p>
<p>Se você é Rubyista vai se sentir bastante em casa com a maior parte da biblioteca padrão. Vai sentir falta de uma coisa ou outra, mas na maior parte do tempo vai parecer Ruby. O <a href="http://crystal-lang.org/docs"target="_blank" rel="noopener">Guia oficial</a> é bom o suficiente para começar e tem uma <a href="http://crystal-lang.org/api"target="_blank" rel="noopener">documentação de API</a> abrangente, suficiente para checar se aquela API favorita do Ruby está lá ou não, e se não estiver, quais são os substitutos. Na maioria das vezes você vai pensar <em>&ldquo;hmm, em Ruby eu usaria Enumerator#each &hellip; e sim, com certeza, tem Enumerator#each como esperado.&rdquo;</em> A documentação da API está precisando de mais carinho, então se você quer contribuir, esse é um bom lugar para começar.</p>
<p>Se você quer dar uma olhada em uma lista curada de bibliotecas e aplicações Crystal, vá direto no <a href="http://awesome-crystal.com/"target="_blank" rel="noopener">&ldquo;Awesome Crystal&rdquo;</a>. Você também pode ver os mais dinâmicos <a href="https://github.com/trending/crystal"target="_blank" rel="noopener">&ldquo;Trending Projects in Github&rdquo;</a>.</p>
<p>Na maior parte, sua proficiência em Ruby vai compensar para construir sistemas pequenos onde performance bruta e concorrência sejam mesmo necessárias. Esse é exatamente o caso de uso para o port do <a href="https://github.com/mperham/sidekiq.cr"target="_blank" rel="noopener">Sidekiq.cr</a> do Mike Perham. Imagine que você tem gigabytes de arquivos para processar. Ou você tem milhares de sites externos para crawlear, parsear e organizar. Ou você tem petabytes de dados no seu banco esperando para serem processados. Essas são tarefas em que MRI Ruby não vai te ajudar, mas Crystal pode muito bem ser a resposta rápida.</p>
<p>Você até pode pegar um dos vários micro frameworks web como o Kemal e fazer deploy dos seus provedores de API HTTP <a href="http://crystal-lang.org/2016/05/26/heroku-buildpack.html"target="_blank" rel="noopener">direto no Heroku</a> agora. Isso te dá níveis de performance estilo Go, e isso é <strong>muito</strong> atraente para mim, pois também detesto a sintaxe arcaica do Go.</p>
<p>Então, Rust, Go, Javascript, todas muito rápidas, mas com sintaxes bem questionáveis e nem tão agradáveis. Elas são bem mais maduras, e seus ecossistemas são muito maiores. Mas, a menos que eu realmente precise, prefiro escolher Crystal para os casos de uso que descrevi acima, e acho que você também vai encontrar bons usos para ele.</p>
<p>Update: depois que postei, o criador do Crystal, @Asterite, teve algumas coisas a acrescentar também:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/waj">@waj</a> so it&#39;s likely that we&#39;ll have something similar. We aren&#39;t there yet because we are not Google, Mozilla nor Apple</p>&mdash; Ary Borenszweig (@asterite) <a href="https://twitter.com/asterite/status/737778080085868545">May 31, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
]]></content:encoded><category>crystal</category><category>ruby</category><category>linguagens-de-programacao</category></item></channel></rss>