<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ítalo Epifânio</title>
    <description>The latest articles on DEV Community by Ítalo Epifânio (@itepifanio).</description>
    <link>https://dev.to/itepifanio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F282296%2F37eeb224-7f83-439a-8789-c5bb81a74fe7.jpeg</url>
      <title>DEV Community: Ítalo Epifânio</title>
      <link>https://dev.to/itepifanio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itepifanio"/>
    <language>en</language>
    <item>
      <title>Programação letrada com Jupyter Notebook e Nbdev</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Tue, 08 Nov 2022 21:44:49 +0000</pubDate>
      <link>https://dev.to/itepifanio/programacao-letrada-com-jupyter-notebook-e-nbdev-43ab</link>
      <guid>https://dev.to/itepifanio/programacao-letrada-com-jupyter-notebook-e-nbdev-43ab</guid>
      <description>&lt;p&gt;O ambiente de desenvolvimento &lt;a href="https://jupyter.org/"&gt;Jupyter notebook&lt;/a&gt; é muito popular entre a comunidade científica. Com essa ferramenta você consegue escrever texto em formato markdown e código Python (R ou Julia) no mesmo arquivo, algo que pode melhorar a legibilidade e entendimento do seu programa. Esse paradigma, que mistura texto e código, é chamado de &lt;a href="https://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_letrada"&gt;programação letrada&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Um exemplo de Jupyter notebook pode ser vista na imagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EtiaLEpw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ax8g9xnui7bgkxxghu87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EtiaLEpw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ax8g9xnui7bgkxxghu87.png" alt="Parte de um Jupyter notebook que inclui uma seção com texto em formato markdown e código python" width="880" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Programação letrada é um paradigma muito bem conceituado, formalmente discutido por muitos pesquisadores respeitados mundialmente como o &lt;a href="https://pt.wikipedia.org/wiki/Donald_Knuth"&gt;Donald Knuth&lt;/a&gt;. Ao mesmo tempo, Jupyter notebooks são considerados ineficientes para o desenvolvimento de software. Essa controversia levou a criação da famosa palestra "&lt;a href="https://www.youtube.com/watch?v=7jiPeIFXb6U"&gt;Eu não gosto de notebooks&lt;/a&gt;", respondida por outra palestra intitulada "&lt;a href="https://www.youtube.com/watch?v=7jiPeIFXb6U"&gt;Eu gosto de Jupyter notebooks&lt;/a&gt;" que gerou &lt;a href="(https://www.fast.ai/2020/10/28/code-of-conduct/)"&gt;bastante drama&lt;/a&gt; na comunidade Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AjtOe_8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwvrn048yj8rcr1sw430.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AjtOe_8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwvrn048yj8rcr1sw430.jpg" alt='Capa do vídeo "Eu gosto de Jupyter notebooks" que, além do título garrafal, mostra ao fundo o Lula Molusco em cima de um palco, se esquivando de tomates sendo jogados pela plateia' width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jupyter notebooks tem sido limitados a exploração de dados, pequenos scripts e materiais educacionais. A recente introdução da &lt;a href="https://nbdev.fast.ai/"&gt;Nbdev&lt;/a&gt;, uma biblioteca python, expandiu as possibilidades desses notebooks. Nbdev permite o desenvolvimento e distribuição de pacotes python enquanto se beneficia do paradigma de programação letrada, permitindo a execução de testes, documentação do software em tempo real (unindo texto e código), publicação da documentação como arquivos estáticos ou simplesmente o desenvolvimento de material técnico.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nbdev permite que você conte uma história com o seu código. É uma ferramenta prática e poderosa!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nbdev tem se provado útil no desenvolvimento de grandes e sérios projetos como o &lt;a href="https://github.com/fastai/fastai"&gt;FastAi&lt;/a&gt;. A utilização da biblioteca Nbdev com Jupyter notebooks permite o desenvolvimento de softwares com qualidade e com os benefícios da programação letrada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiência
&lt;/h2&gt;

&lt;p&gt;Minha primeira experiência com Nbdev foi a de estranhamento. Trabalhar com Jupyter notebooks para desenvolvimento sério de software não me parecia natural, algo esperado, afinal é um novo paradigma de programação.&lt;/p&gt;

&lt;p&gt;A ferramenta em que estou trabalhando atualmente, &lt;a href="https://github.com/palaimon/ipyannotator"&gt;Ipyannotator&lt;/a&gt;, é um framework de anotações open source que contém interfaces gráficas que funcionam no Jupyter notebook. O desenvolvimento do Ipyannotator só foi possível graças a ferramenta Nbdev que permitiu o time testar o software em notebooks e exportar o código como uma biblioteca python.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Depois de um ano desenvolvendo software com Jupyter notebook eu sinto que isso melhorou minha produtividade&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esse paradigma de desenvolvimento de software me permitiu manter a minha documentação atualizada, uma vez que a documentação está naturalmente acima do meu bloco de código Python. O paradigma também reduziu o lapso mental de voltar a um código antigo, facilitando o entendimento do código uma vez que decisões prévias estavam documentadas em blocos markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarto
&lt;/h2&gt;

&lt;p&gt;No dia 28 de Julho, Nbdev lançou sua segunda versão, incorporando outra ferramenta open source no seu arsenal: &lt;a href="https://quarto.org/"&gt;Quarto&lt;/a&gt;. Quarto é uma ferramenta que permite a criação de conteúdo técnico utilizando Python, R ou Julia, utilizando diversos tipos de arquivo, seja markdown, Jupyter notebooks, R notebooks e permitindo exportar esses arquivos como arquivos, websites, blogs, HTML, PDFs, EPubs e até slides.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quarto é uma solução completa para escrever qualquer material técnico, utilizando as ferramentas que você já conhece.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essa ferramenta é tão legal que estou planejando migrar &lt;a href="https://itepifanio.github.io"&gt;o meu blog pessoal&lt;/a&gt; de Jekyll para Quarto. O design clean, de fácil usabilidade e com a funcionalidade de executar código embutido em blocos de texto, se encaixa perfeitamente em qualquer requisito de escrita técnica.&lt;/p&gt;

&lt;p&gt;Nbdev notou o quão poderoso Quarto pode ser. Essa adição do Quarto ao core do Nbdev ajudou a renderizar melhor sites e documentações. A primeira versão da biblioteca Nbdev já continha essas funcionalidades, mas elas eram rudimentares e precisavam de diversas gambiarras. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Desenvolvimento com Nbdev e Quarto pode melhorar a produtividade de diversos times, ajudando a documentar e contar a história do seu código. Essas ferramentas ajudam a expandir as possibilidades de desenvolvimento de software, assim como foi o caso do já exemplificado &lt;a href="https://github.com/palaimon/ipyannotator"&gt;Ipyannotator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Como qualquer ferramenta, Nbdev também tem lados negativos. Um deles é a falta do auto completar em Jupyter notebooks, algo que pode ser remediado com a utilização de extensões do Visual Code, por exemplo. Outro problema é que, por conta dos Jupyter notebooks serem escritos em formato json, um formato que dificulta a resolução de conflitos git por conta de todos os metadados extras. Dificultar a resolução de conflitos minimiza as chances de grandes equipes de trabalhar no mesmo software.&lt;/p&gt;

&lt;p&gt;Felizmente, a adição de Quarto mostra que Nbdev já planeja dar suporte a outros tipos de arquivos além de Jupyter notebooks. Isso permitiria que times maiores conseguissem desenvolver software com programação letrada, diminuindo o maior defeito da ferramenta, que é a resolução de conflitos.&lt;/p&gt;

&lt;p&gt;Se você se interessou em tentar usar Nbdev você pode checar as seguintes referências:&lt;br&gt;
&lt;a href="https://www.fast.ai/2022/07/28/nbdev-v2/"&gt;Nbdev+Quarto: Uma nova arma secreta de produtividade&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Se ficou curioso sobre como é o código Jupyter notebook para desenvolvimento de software você pode olhar o &lt;a href="https://github.com/palaimon/ipyannotator/tree/main/nbs"&gt;código do Ipyannotator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Post originalmente postado no blog Palaimon: &lt;a href="https://blog.palaimon.io/posts/literate-programming-nbdev/"&gt;https://blog.palaimon.io/posts/literate-programming-nbdev/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jupyter</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Objetos hashable em Python</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:33:00 +0000</pubDate>
      <link>https://dev.to/itepifanio/objetos-hashable-em-python-3gj1</link>
      <guid>https://dev.to/itepifanio/objetos-hashable-em-python-3gj1</guid>
      <description>&lt;p&gt;Hash de objetos é uma representação númerica inteira que é obtida utilizando o &lt;a href="https://docs.python.org/3/reference/datamodel.html#special-method-names"&gt;dunder method&lt;/a&gt; &lt;code&gt;__hash__&lt;/code&gt;. Compreender esse conceito ajuda a entender como as estruturas de dados Python funcionam, uma vez que o hash dos objetos são utilizados internamente pela linguagem. &lt;/p&gt;

&lt;p&gt;Programadores Python costumam se deparar com o conceito de hash de objetos quando tentam armazenar um objeto sem hash em uma estrutura de dados da linguagem. Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pessoa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pessoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ítalo Epifânio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1010101010"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima define uma classe pessoa e um objeto &lt;code&gt;p&lt;/code&gt; do tipo pessoa. Ao tentar adicionar uma pessoa ao conjunto &lt;code&gt;pessoas&lt;/code&gt; o seguinte erro é lançado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
  File &lt;span class="s2"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;, line 1, &lt;span class="k"&gt;in&lt;/span&gt; &amp;lt;module&amp;gt;
TypeError: unhashable &lt;span class="nb"&gt;type&lt;/span&gt;: &lt;span class="s1"&gt;'Pessoa'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso acontece porque a estrutura de dados "conjunto" do Python utiliza o hash em suas tabelas internas para encontrar o valor do objeto rapidamente. Como nosso objeto não tem hash, o erro acima é lançado.&lt;/p&gt;

&lt;p&gt;Para adicionar um hash a um objeto implementa-se as funções &lt;code&gt;__hash__&lt;/code&gt; e &lt;code&gt;__eq__&lt;/code&gt; simultaneamente (caso esteja utilizando Python 2 a função &lt;code&gt;__ne__&lt;/code&gt; também deve ser adicionada). No exemplo a seguir modificamos nossa classe anterior para adquirir essa propriedade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pessoa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__hash__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__eq__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;mesma_classe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;
        &lt;span class="n"&gt;mesmo_cpf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mesma_classe&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;mesmo_cpf&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pessoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ítalo Epifânio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"101.010.101-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o código consegue ser executado sem o erro anterior. Se executarmos &lt;code&gt;print(pessoas)&lt;/code&gt; veremos que o conjunto contém o seguinte valor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;Pessoa&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Ítalo Epifânio'&lt;/span&gt;, &lt;span class="nv"&gt;cpf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1010101010&lt;span class="o"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Um objeto é dito hashable se o valor de hash nunca é modificado durante sua fase de vida&lt;br&gt;
-- &lt;cite&gt; &lt;a href="https://docs.python.org/3/glossary.html#term-hashable"&gt;Python Docs&lt;/a&gt; &lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apenas implementar os dunder métodos acima não garante que o objeto é hashable. É preciso garantir o valor de hash desse objeto jamais sejá alterado pois isso pode levar a comportamentos não esperados, por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pessoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ítalo Epifânio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"101.010.101-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# retorna True
&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"999.999.999-99"&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# retorna False
&lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pessoas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# retorna {Pessoa(nome='Ítalo Epifânio', cpf='999.999.999-99')}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao alterar o cpf do objeto &lt;code&gt;p&lt;/code&gt; note que o objeto não é mais encontrado na estrutura de dados conjunto (a segunda chamada do &lt;code&gt;print(p in pessoas)&lt;/code&gt; retorna falso) e quando lista-se os valores do conjunto &lt;code&gt;pessoas&lt;/code&gt; nota-se que há ainda um valor lá.&lt;/p&gt;

&lt;p&gt;Em resumo: você não pode basear hash de objetos em valores mutáveis. Se o atributo de um objeto pode ser modificado durante seu ciclo de vida comportamentos inesperados podem acontecer.&lt;/p&gt;

</description>
      <category>python</category>
      <category>oop</category>
    </item>
    <item>
      <title>Horizontal scroll with lazy loading</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Sun, 31 Oct 2021 20:14:19 +0000</pubDate>
      <link>https://dev.to/itepifanio/horizontal-scroll-with-lazy-loading-578c</link>
      <guid>https://dev.to/itepifanio/horizontal-scroll-with-lazy-loading-578c</guid>
      <description>&lt;p&gt;Recently I came across with the following scenario: a management software of workflows that allow the user to dynamic create its kanbans (like the one in the image below). One of the users set its kanban to have 38 columns.&lt;/p&gt;

&lt;p&gt;The software was design in a way that every kanban column made a request to the backend, in this scenario 38 new requests were made every time a user access the kanban page. This not only overload the server but also the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui3slceaoxqovpxm1uo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui3slceaoxqovpxm1uo.jpg" alt="Kanban image with five columns. The columsn in the sequence: stories, todo, in progress, testing, done"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First we needed to decrease the number of requests, limiting the requests to the columns that were visible to the user. After that we had to make sure that if the user scrolls to the end of the page at once, the columns would not request the data unless they were visible for a certain amount of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limiting loading to visible cards
&lt;/h2&gt;

&lt;p&gt;Javascript offers an API called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver" rel="noopener noreferrer"&gt;IntersectionObserver&lt;/a&gt; that allow us to watch HTML elements and check its visibility on screen. The code below shows its most basic operation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onIntersection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onIntersection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.my-elements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The function &lt;code&gt;onIntersection&lt;/code&gt; is responsable for the logic that will be applied to the visible elements, it takes a list of elements and checks that if they're visible  (&lt;code&gt;element.isIntersecting&lt;/code&gt;) then something will be done, in which case a console message is displayed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;IntersectionObserver&lt;/code&gt; API call is made and set to the &lt;code&gt;observer&lt;/code&gt; variable. The &lt;code&gt;observer&lt;/code&gt; object will then be able to observe elements in the HTML and execute logic only when they are visible on the user's screen. In my case, for the giant kanban, this was enough to limit the 38 requests as the page loaded to just 5, but if the user scrolled the page to its end at once several requests would be made (the other 33 requests).&lt;/p&gt;

&lt;h2&gt;
  
  
  Load only after a certain time of the visible element on the page
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;IntersectionObserver&lt;/code&gt; API has a &lt;a href="https://web.dev/intersectionobserver-v2/" rel="noopener noreferrer"&gt;version 2&lt;/a&gt; that allows capturing how long a certain HTML element was visible on the screen and this would easily solve the element loading problem HTML only after a certain amount of time. However, version 2 still doesn't have its implementations compatible with most browsers.&lt;/p&gt;

&lt;p&gt;In my specific case I was using a parent component that rendered the 38 child elements and I couldn't check when those 38 child elements were finished rendering to observe them with the &lt;code&gt;InsertersectionObserver&lt;/code&gt;, so control how long each element was visible in the screen got a little more complicated.&lt;/p&gt;

&lt;p&gt;Each of the 38 child elements knew when they were rendering themselves, so you could use the &lt;code&gt;IntersectionObserver&lt;/code&gt; internally on each of them. Using the &lt;code&gt;setTimeout&lt;/code&gt; function of the javascript you can observe the element after a certain time specified in milliseconds.&lt;/p&gt;

&lt;p&gt;We have 38 elements, but most are not visible on the screen and become visible when scrolling, with the &lt;code&gt;setTimeout&lt;/code&gt; delay this action still takes some time to be executed. During scrolling, when the element visible on the screen has not yet triggered the specified &lt;code&gt;setTimeout&lt;/code&gt; and the user has already scrolled to the next element, it is possible to remove the timeout of the previous element from the stack and then load only the next element. The following code shows this strategy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border border-black m-1 p-10 min-w-max h-10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
       &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
       &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;() =&amp;gt; {
           let timeout;
           let loadColumn = function (elements) {
               clearTimeout(timeout);

               timeout = setTimeout(function() {
                   elements.forEach(element =&amp;gt; {
                       if (element.isIntersecting) {
                           // do something
                           observer.unobserve(element.target);
                       }
                   });
               }, 750);
           }

           let observer = new IntersectionObserver(loadColumn);
           let target = $el;
           observer.observe(target);
       }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When the component is loaded into the page it already starts looking at itself using the &lt;code&gt;loadColumn&lt;/code&gt; function. Such function removes the previous timeouts (which were not triggered) from the execution stack and adds a new timeout which after 750 milliseconds does something and removes the observation to not redo the same logic if the element becomes visible again.&lt;/p&gt;

&lt;p&gt;In my case the logic was the request to the server so I only needed to load the data once and then ignore it if the element was visible again on the page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did you find the above code syntax strange? This javascript microframework is called &lt;a href="https://alpinejs.dev/" rel="noopener noreferrer"&gt;AlpineJS&lt;/a&gt; and that's what I used to develop the complete solution. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simpler POC, without the request to the server, can be seen below. After being visible on your screen the white squares will turn black indicating the request to the server.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/itepifanio/embed/PoKqGPm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you are interested in seeing a solution with vanilla javascript &lt;a href="https://www.codeguage.com/tutorials/lazy-loading/intersection-observer" rel="noopener noreferrer"&gt;this was my reference&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webpack</category>
      <category>alpinejs</category>
      <category>alpine</category>
    </item>
    <item>
      <title>Carregamento lento com scroll horizontal</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Wed, 13 Oct 2021 03:50:31 +0000</pubDate>
      <link>https://dev.to/itepifanio/carregamento-lento-com-scroll-horizontal-4740</link>
      <guid>https://dev.to/itepifanio/carregamento-lento-com-scroll-horizontal-4740</guid>
      <description>&lt;p&gt;Recentemente me deparei com o seguinte cenário: um sistema de gerenciamentos de fluxos de trabalho que permite configuração  de kanbans, como o da imagem abaixo, sendo que um usuário em particular configurou seu kanban com 38 colunas.&lt;/p&gt;

&lt;p&gt;Cada coluna do kanban realizava uma requisição e do jeito que o sistema tinha sido desenvolvido gerava-se 38 requisições assim que a página era carregada, o que acabava espancando o banco de dados e o servidor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ntTMQ0t6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ui3slceaoxqovpxm1uo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ntTMQ0t6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ui3slceaoxqovpxm1uo.jpg" alt="Ilustração de um kanban com cinco colunas na sequência: stories, todo, in progress, testing, done"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inicialmente precisavamos diminuir a quantidade de requisições, limitando apenas aos cards visíveis na tela do usuário. Depois precisavamos fazer com que, caso o usuário rolasse para o fim da página de uma vez, as colunas que ficaram visíveis não carregassem a menos que estivessem a um certo tempo visíveis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitando o carregamento aos cards visíveis
&lt;/h2&gt;

&lt;p&gt;O javascript oferece uma API chamada &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver"&gt;IntersectionObserver&lt;/a&gt; que permite monitorar elementos HTML e verificar sua visibilidade na tela. O código abaixo mostra o funcionamento mais básico dela.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onIntersection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onIntersection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.my-elements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função &lt;code&gt;onIntersection&lt;/code&gt; é responsável pela lógica que será aplicada aos elementos visiveis, ela recebe uma lista de elementos e verifica que se forem visiveis (&lt;code&gt;element.isIntersecting&lt;/code&gt;) então algo será feito, nesse caso uma mensagem no console é exibida.&lt;/p&gt;

&lt;p&gt;A chamada da API &lt;code&gt;IntersectionObserver&lt;/code&gt; é feita e atribuida a variável &lt;code&gt;observer&lt;/code&gt;. O objeto &lt;code&gt;observer&lt;/code&gt; conseguirá a partir dali observar elementos no HTML e executar uma lógica somente quando eles forem visíveis na tela do usuário. No meu caso, do kanban gigante, isso foi suficiente para limitar as 38 requisições assim que a página carregava para apenas 5, mas caso o usuário rolasse a página rapidamente várias requisições seriam feitas, ou seja, se eu fosse até o fim da página de uma vez as outras 33 requisições seriam chamadas também de uma vez só.&lt;/p&gt;

&lt;h2&gt;
  
  
  Carregamento apenas após certo tempo do elemento visível na página
&lt;/h2&gt;

&lt;p&gt;A API &lt;code&gt;IntersectionObserver&lt;/code&gt; tem uma &lt;a href="https://web.dev/intersectionobserver-v2/"&gt;versão 2&lt;/a&gt; que permite a captura de quanto tempo um certo elemento HTML ficou visível na tela e isso resolveria facilmente o problema de carregar o elemento HTML apenas depois de certo tempo. Entretanto, a versão 2 ainda não tem suas implementações compativeis com a maioria dos navegadores.&lt;/p&gt;

&lt;p&gt;No meu caso específico eu estava utilizando um componente pai que renderizava os 38 elementos filhos e eu não conseguia verificar quando esses 38 elementos filhos terminaram de ser renderizados para observa-los com o &lt;code&gt;InsertersectionObserver&lt;/code&gt;, então controlar o tempo que cada elemento ficou visível na tela ficou um pouco mais complicado.&lt;/p&gt;

&lt;p&gt;Cada um dos 38 elementos filhos sabiam quando eles mesmos eram renderizados, então conseguia-se utilizar a &lt;code&gt;IntersectionObserver&lt;/code&gt; internamente em cada um deles. Utilizando a função &lt;code&gt;setTimeout&lt;/code&gt; do javascript consegue-se observação o elemento após um certo tempo especificado em milisegundos.&lt;/p&gt;

&lt;p&gt;Temos 38 elementos ao todo, só que a maioria não é visível na tela e se torna visível ao scrollar, com o delay do &lt;code&gt;setTimeout&lt;/code&gt; essa ação leva ainda algum tempo a ser executada. Durante o scroll, quando o elemento visível na tela ainda não disparou o &lt;code&gt;setTimeout&lt;/code&gt; especificado e o usuário já scrollou para um elemento seguinte consegue-se remover o timeout do elemento anterior da pilha de execução e então carregar somente o elemento seguinte. O código a seguir mostra essa estratégia.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border border-black m-1 p-10 min-w-max h-10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
       &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
       &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;() =&amp;gt; {
           let timeout;
           let loadColumn = function (elements) {
               clearTimeout(timeout);

               timeout = setTimeout(function() {
                   elements.forEach(element =&amp;gt; {
                       if (element.isIntersecting) {
                           // do something
                           observer.unobserve(element.target);
                       }
                   });
               }, 750);
           }

           let observer = new IntersectionObserver(loadColumn);
           let target = $el;
           observer.observe(target);
       }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando o componente é carregado na página ele já começa a observar a si mesmo utilizando a função &lt;code&gt;loadColumn&lt;/code&gt;. Tal função remove os timeouts anteriores (que não foram acionados) da pilha de execução e adiciona um novo timeout que após 750 milisegundos faz algo e remove a observação para não refazer a mesma lógica se o elemento se tornar visível novamente.&lt;/p&gt;

&lt;p&gt;No meu caso a lógica era a requisição para o servidor então eu só precisava carregar o dado uma vez e depois ignorar se o elemento ficasse visível novamente na página, por isso ele remove a própria observação.&lt;/p&gt;

&lt;p&gt;Achou a sintaxe do código acima estranha? Esse microframework javascript se chama &lt;a href="https://alpinejs.dev/"&gt;AlpineJS&lt;/a&gt; e foi o que utilizei para desenvolver a solução completa. Uma POC mais simples, sem a requisição pro servidor, pode ser vista logo abaixo. Após ficar visível na sua tela os quadrados brancos se tornarão pretos indicando a requisição pro servidor.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/itepifanio/embed/PoKqGPm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Caso se interesse por ver uma solução com vanilla javascript  a minha referência &lt;a href="https://www.codeguage.com/tutorials/lazy-loading/intersection-observer"&gt;foi essa&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>alpinejs</category>
      <category>alpine</category>
    </item>
    <item>
      <title>TALL - Uma nova stack para PHP</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Sat, 20 Jun 2020 02:03:18 +0000</pubDate>
      <link>https://dev.to/itepifanio/tall-uma-nova-stack-para-php-56nn</link>
      <guid>https://dev.to/itepifanio/tall-uma-nova-stack-para-php-56nn</guid>
      <description>&lt;p&gt;&lt;a href="https://tailwindcss.com/"&gt;&lt;strong&gt;T&lt;/strong&gt;ailwind&lt;/a&gt;, &lt;a href="https://github.com/alpinejs/alpine"&gt;&lt;strong&gt;A&lt;/strong&gt;lpinejs&lt;/a&gt;, &lt;a href="https://laravel.com/"&gt;&lt;strong&gt;L&lt;/strong&gt;aravel&lt;/a&gt; e &lt;a href="https://laravel-livewire.com/"&gt;&lt;strong&gt;L&lt;/strong&gt;ivewire&lt;/a&gt; (&lt;strong&gt;TALL&lt;/strong&gt;) é uma solução para desenvolvedores full stack &lt;a href="https://tallstack.dev/"&gt;construída pela comunidade laravel&lt;/a&gt; que foca principalmente no desenvolvimento backend, mas que permite o desenvolvimento de sistemas ditos "reativos".&lt;/p&gt;

&lt;p&gt;A stack TALL é ótima para desenvolvimento rápido de sistemas web. A facilidade de estilizar páginas com o Tailwind, o poder da reatividade do Alpinejs, aliado com a componentização do Livewire, realmente dá um boost de produtividade no desenvolvimento.&lt;/p&gt;

&lt;p&gt;Nesse artigo falarei um pouco sobre as 4 tecnologias e minha experiência com a stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Livewire
&lt;/h2&gt;

&lt;p&gt;É muito comum encontrar sistemas que utilizam Laravel em conjunto com o Vuejs. A utilização de componentes Vue remove muito da responsabilidade da blade, por exemplo, é comum encontrarmos blades do tipo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;layouts&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vue&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vue&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;endsection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caleb, criador do Alpinejs e Livewire, &lt;a href="https://www.youtube.com/watch?v=uQO4Xh1gMpY"&gt;chama&lt;/a&gt; esses componentes de "assassinos de blades". Vue é uma ferramenta poderosa, mas o tempo gasto tratando eventos assíncronos, adicionando v-ifs, atualizando bibliotecas, atrasa o desenvolvimento e torna a experiência um pouco maçante. Parte do atraso mencionado se deve a falta de testes unitários, eu, particularmente, não conheço ninguém que utilize testes unitários com Vue e isso é bem problemático quando encontramos blades e blades como a exibida acima.&lt;/p&gt;

&lt;p&gt;Quanto mais se trabalha com Vue e Laravel mais sentimos a necessidade de escrever um código javascript melhor, aprender Vuex, utilizar testes unitários nele usando Jest ou outra lib. Vamos adicionando camadas e camadas de complexidade para algo que deveria ser simples: requisição e exibição de dados (o pior dos casos é quando regra de negócio entra em front-end, debugar um erro em um código assim é uma grande dor de cabeça).&lt;/p&gt;

&lt;p&gt;Caleb propõe uma solução voltada mais ao backend, intrínseca ao Laravel, o Livewire. É difícil definir a ferramenta, mas é como se escrevessemos código Laravel como componentes. A definição oficial é: &lt;code&gt;Livewire is a full-stack framework for Laravel that makes building dynamic interfaces simple, without leaving the comfort of Laravel.&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O Livewire é ótimo para situações em que o nosso usuário interage com a tela e espera uma ação imediata, ele irá fazer uma requisição ajax e atualizar o front automaticamente (como se a página fosse reativa). Um exemplo, visto abaixo, retirado do &lt;a href="https://medium.com/@branick/search-with-laravel-livewire-cb6dcd4ad541"&gt;artigo do Branick&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U0NtiGG_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/1400/1%2AvTfmPgyEaCmPgqpR8DDWzw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U0NtiGG_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/1400/1%2AvTfmPgyEaCmPgqpR8DDWzw.gif" alt="Search with Laravel Livewire" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O Livewire é uma ferramenta muito poderosa que permite reaproveitar código através dos componentes, além de facilitar e agilizar o desenvolvimento de interfaces ricas, sem ter que chamar bibliotecas JS externas, sem sair do arcabouço Laravel e podendo realizar testes unitários nos componentes. Entretanto todas essas facilidades tem um preço, que é o custo de renderizar novamente HTML toda vez que se faz uma requisição. O Livewire não é uma “bala de prata” e deve ser utilizado com cautela. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind
&lt;/h2&gt;

&lt;p&gt;O Tailwind é um framework CSS com uma abordagem &lt;em&gt;utility-first&lt;/em&gt;&lt;br&gt;
, o código CSS é abstraído no HTML como classes. Por exemplo, o código &lt;code&gt;&amp;lt;div style="background-color:black; border-radius: 100%"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; no Tailwind ficaria &lt;code&gt;&amp;lt;div class="bg-black round-full"&amp;gt;&amp;lt;div&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;O primeiro contato com o framework é estranho porque o HTML se torna extenso de escrever, mas a medida que vamos utilizando vemos o quão ágil é utilizar o Tailwind. As páginas são escritas eficientemente, se inspeciona elemento, muda as classes, copia para o código e em minutos você tem algo funcional que levaria  mais tempo para fazer utilizando Bootstrap.&lt;/p&gt;

&lt;p&gt;Esse esquema quase de prototipação de páginas é ótimo para fábricas de software. Uma vez que os desenvolvedores pegam o jeito como o framework se torna fácil pegar um design do Adobe XD e dar vida no HTML.&lt;/p&gt;
&lt;h2&gt;
  
  
  AlpineJS
&lt;/h2&gt;

&lt;p&gt;O Alpinejs é um microframework JS, reativo, muito parecido com Vue (só que bem mais simples) que facilita manipulações do DOM. Por ter sido criado pela mesma pessoa, o Alpinejs funciona muito bem com integrações com o Livewire. Todos os gargalos de performance encontrados no Livewire eu consegui contornar utilizando o Alpine em conjunto. &lt;/p&gt;

&lt;p&gt;Um exemplo de utilização do Alpine em conjunto com o Tailwind pode ser visto abaixo. Note que o código HTML mantém a estilização e o JS embutido, é muito simples e rápido estilizar e modificar o DOM utilizando as duas ferramentas em conjunto.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/itepifanio/embed/VwvreoP?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;A curva de aprendizado do framework é muito baixa porque ele é bem pequeno, além de procurar se aproximar bastante da sintaxe do Vue (velho conhecido dos devs fullstack Laravel).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Estou trabalhando com a stack a cerca de três ou quatro meses, escrevendo uma nova versão de um sistema que antes era feito com Laravel e Vue. O design foi criado no Adobe XD e em questão de uma a duas semanas já tínhamos passado completamente pro Tailwind. A utilização do Livewire foi feita a medida da necessidade, em geral, situações que exigiria requisições ajax ou que apelariamos para o Vue, se tornaram componentes Livewire.&lt;/p&gt;

&lt;p&gt;A impressão é que o novo sistema de componentes blade do Laravel 6 e as novas funcionalidades do Laravel 7 também colaboraram muito para essa nova forma de escrever sistemas, tornando natural codar no Laravel componentizado e associar o Livewire nas blades. &lt;/p&gt;

&lt;p&gt;No começo do projeto eu não conhecia Livewire, Alpinejs e nem o Tailwind, mas é muito bom sair do conforto com esses três. Como o Tailwind é mais "baixo nível" que o Bootstrap eu acabei aprendendo flexbox e vários outros detalhes de CSS que eu desconhecia. O mesmo aconteceu com o Alpinejs, por ser pequeno boa parte do código feito com ele é JS puro.&lt;/p&gt;

&lt;p&gt;O Tall potencializa as tecnologias básicas que usamos no dia a dia (JS, CSS, PHP) tornando o desenvolvimento bem mais divertido e sem necessidade de adicionar bibliotecas terceiras que acabam complicando a manutenção posterior do sistema. Além disso, o que mais curto na stack, é que se aproveita ao máximo o uso do Laravel.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Uma visão geral sobre Alpine.js + Tailwind</title>
      <dc:creator>Ítalo Epifânio</dc:creator>
      <pubDate>Sun, 03 May 2020 01:11:46 +0000</pubDate>
      <link>https://dev.to/itepifanio/uma-visao-geral-sobre-alpine-js-tailwind-18mf</link>
      <guid>https://dev.to/itepifanio/uma-visao-geral-sobre-alpine-js-tailwind-18mf</guid>
      <description>&lt;p&gt;Alpine.js é um framework reativo como o Vue e o React só que com um custo mínimo, zipado o alpine ocupa 4Kb. Para quem programa e utiliza Vue a sintaxe é praticamente a mesma, como o próprio autor aponta na &lt;a href="https://github.com/alpinejs/alpine"&gt;documentação&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recentemente comecei a trabalhar com o Tailwind, um framework css de "baixo nível" que utiliza classes como açucar sintático para o puro css. A primeira coisa que pensei quando conheci a ferramenta foi "por que alguém se submeteria a escrever tanta coisa no html?", veja, o código parece sujo no começo, mas os componentes pre-desenhados, o esquema de responsividade deles (melhor até que o do bootstrap), além do código estupidamente explicito, me ganhou. As telas realmente são feitas mais rapidamente e eu não tenho mais que parar, ir numa folha de estilo, procurar qual parte do código tá modificando aquilo, caçar se outra folha está sobrescrevendo o estilo, etc.&lt;/p&gt;

&lt;p&gt;O Alpine.js é como o tailwind, deixa explícito o que está sendo feito, a curva de aprendizado é muito curta e é ótimo para ser utilizado em conjunto com o Tailwind. Abaixo o código de um simples todo list feito com o micro framework: &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/itepifanio/embed/VwvreoP?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;No exemplo são utilizados conceitos simples, o &lt;code&gt;x-data="{ items: [], item: '' }"&lt;/code&gt; declara um escopo pro componente, como se fosse uma variável. O &lt;code&gt;x-for&lt;/code&gt; cria o dom dinamicamente iterando sobre a variável &lt;code&gt;items&lt;/code&gt;. O &lt;code&gt;x-model&lt;/code&gt; conecta as modificações do input com a variável item e o &lt;code&gt;@click&lt;/code&gt; ativa o evento que insere um valor no item.  &lt;/p&gt;

&lt;p&gt;Eu realmente estou gostando de trabalhar com ambas as ferramentas e acho que ambas me tiraram da zona de conforto das camadas de abstrações que outros frameworks oferecem, então você realmente aprende css e js utilizando o alpine + tailwind. O projeto que me fez conhecer essas tecnologias utiliza o Laravel (framework PHP) com o Livewire (uma abstração do laravel que componentiza código PHP), tailwind e alpine js, uma stack para ser comentada em outros posts.&lt;/p&gt;

</description>
      <category>alpinejs</category>
      <category>javascript</category>
      <category>tailwindcss</category>
      <category>css</category>
    </item>
  </channel>
</rss>
