Desenvolvimento

Desenvolvimento

Smoosh Gate – A lição que precisamos aprender

12 jun, 2018
Publicidade

Já faz um tempo que uma proposta feita para o JavaScript está gerando um rebuliço na comunidade. O que esta proposta basicamente diz, é a implementação de um novo método para todos os tipos Array, método este que todos conhecemos como flatten.

Outro método também proposto é o flatMap, que vamos explicar a seguir.

Flatten

Quem já trabalhou com JavaScript por um tempo – ou qualquer outra linguagem, na verdade – já deve ter caído em uma situação onde temos um array de arrays, e queremos transformar isso tudo em um array de nível único, por exemplo:

1
let a = [<span style="color: #6600ee; font-weight: bold;">1</span>, [<span style="color: #6600ee; font-weight: bold;">2</span>, <span style="color: #6600ee; font-weight: bold;">3</span>], <span style="color: #6600ee; font-weight: bold;">4</span>, [<span style="color: #6600ee; font-weight: bold;">5</span>, <span style="color: #6600ee; font-weight: bold;">6</span>]]

Aqui temos uma representação que nos daria algo desse tipo se fossemos transformar em uma árvore:

1
2
3
4
5
6
a <span style="color: #333333;">=</span> <span style="color: #007020;">Array</span>(
 <span style="color: #0000dd; font-weight: bold;">0</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">1</span>,
 <span style="color: #0000dd; font-weight: bold;">1</span> <span style="color: #333333;">=&gt;</span> <span style="color: #007020;">Array</span>(<span style="color: #0000dd; font-weight: bold;">0</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">2</span>, <span style="color: #0000dd; font-weight: bold;">1</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">3</span>),
 <span style="color: #0000dd; font-weight: bold;">2</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">4</span>,
 <span style="color: #0000dd; font-weight: bold;">3</span> <span style="color: #333333;">=&gt;</span> <span style="color: #007020;">Array</span>(<span style="color: #0000dd; font-weight: bold;">0</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">5</span>, <span style="color: #0000dd; font-weight: bold;">1</span> <span style="color: #333333;">=&gt;</span> <span style="color: #0000dd; font-weight: bold;">6</span>)
)

E o que queremos é ter um array simples a = [1,2,3,4,5,6]. Para isso usamos o que é chamado de flatten, ou “achatar” se formos traduzir literalmente.

Existem N bibliotecas que fazem isso hoje. LoDash é uma delas (possui um método flatten e também um flattenDeep para achatar arrays que tem outros arrays em níveis mais baixos). Outra biblioteca famosa é o MooTools, que já é usada há muitos anos, desde antes das padronizações atuais.

O que esse método faz é basicamente isso:

1
2
3
4
5
6
7
8
9
<span style="color: #888888;">// Achata um nível:</span>
<span style="color: #008800; font-weight: bold;">const</span> array <span style="color: #333333;">=</span> [<span style="color: #0000dd; font-weight: bold;">1</span>, [<span style="color: #0000dd; font-weight: bold;">2</span>, [<span style="color: #0000dd; font-weight: bold;">3</span>]]];
array.flat();
<span style="color: #888888;">// → [1, 2, [3]], veja que [3] está dentro de [2, [3]], ou seja, outro nível</span>

<span style="color: #888888;">// Achata recursivamente até não ter mais arrays aninhados:</span>
<span style="color: #888888;">// Mesmo que o flattenDeep</span>
array.flat(<span style="color: #008800; font-weight: bold;">Infinity</span>);
<span style="color: #888888;">// → [1, 2, 3]</span>

FlatMap

O caso do Array.prototype.flatMap é um pouco mais específico. O que este método faz, na verdade, é aplicar o map em um array, mas todo o seu resultado é “achatado” em um array de único nível:

1
2
[<span style="color: #0000dd; font-weight: bold;">2</span>, <span style="color: #0000dd; font-weight: bold;">3</span>, <span style="color: #0000dd; font-weight: bold;">4</span>].flatMap((n) <span style="color: #333333;">=&gt;</span> [x, x<span style="color: #333333;">*</span><span style="color: #0000dd; font-weight: bold;">2</span>])
<span style="color: #888888;">// → [2, 4, 3, 6, 4, 8]</span>

O problema

Este recurso está em draft, mas no estágio 3, o que significa que ele já é um candidato a entrar na linguagem, precisando apenas de um refinamento maior em questão de implementação e usuários, conforme este documento mostra.

Por ser um recurso de estágio 3, o Firefox resolveu implementar a funcionalidade na sua versão nightly, que é uma versão extremamente beta do navegador, e isso causou a quebra de um script em pelo menos um site popular, justamente porque esse nome flatten é o mesmo nome utilizado pelo MooTools, como já havia acontecido antes.

Para resolver esse problema – que impede que a funcionalidade seja publicada – o autor, brincando, propôs que o nome fosse alterado de flatten para smoosh. Essa piada claramente não foi muito bem entendida pelo pessoal, que começou a achar que, realmente, o nome do novo método seria Array.prototype.smoosh.

Mas o que o MooTools faz para causar esse problema?

A implementação do método no MooTools em si, não é o problema. O que a biblioteca basicamente faz é substituir Array.prototype.flatten com uma implementação própria (não presente no documento padrão). Até ai, não temos um grande problema em mãos. Ele realmente começa quando o MooTools copia todos os seus métodos customizados para uma API própria chamada Elements.prototype com esse código:

1
2
3
<span style="color: #008800; font-weight: bold;">for</span> (<span style="color: #008800; font-weight: bold;">var</span> key <span style="color: #008800; font-weight: bold;">in</span> <span style="color: #007020;">Array</span>.prototype) {
    Elements.prototype[key] <span style="color: #333333;">=</span> <span style="color: #007020;">Array</span>.prototype[key]
}

Isso afeta a implementação por um motivo bastante subjetivo, o for-in itera somente por propriedades enumeráveis (como você pode ver nesse meu artigo sobre Symbols), propriedades estas que não incluem métodos nativos como o Array.prototype.sort, mas incluem métodos criados pelos usuários, por exemplo: Array.prototype.meuMetodo = qualquercoisa. Mas, se você sobrescreve uma propriedade não enumerável, como o Array.prototype.sort = outrometodo, ela ainda continua não enumerável.

Atualmente, Array.prototype.flatten = implementacaoDoMooTools cria uma propriedade enumerável chamada flatten, que é copiada para Elements.prototype, como vimos acima. Mas se os browsers publicarem uma versão nativa do flatten, disponível em uma propriedade não enumerável do Array.prototype, ela não vai mais ser copiada para Elements.prototype, ou seja, qualquer código que depende dessa implementação está agora quebrado.

A solução mais óbvia seria transformar Array.prototype.flatten em uma propriedade enumerável, certo? Errado. Se isso acontecer, todos os sites que hoje usam for-in para iterar por um array (o que é uma má prática, conforme você pode ver neste outro artigo que escrevi sobre iterators, mas enfim, acontece) iriam ganhar uma iteração nova por causa da propriedade flatten que começaria a existir do nada.

Por que simplesmente não quebramos a Web?

Poucas pessoas vão lembrar disso, mas muito tempo antes de o HTML5 existir, o filme Space Jam, da Warner, foi lançado (25 de dezembro de 1996). Com ele, um site do filme foi ao ar, e este site continua funcionando da mesma maneira como há 22 anos atrás. Será que alguém andou dando suporte para o site de um filme que muitas pessoas sequer lembram? Ou será outra coisa?

Acontece que “não quebrar a web” (don’t break the web, em inglês) é o princípio número um para HTML, CSS e JS, bem como para qualquer outro padrão de projeto que é largamente utilizado na web. Se um browser cria uma funcionalidade que “quebra a web”, isso é ruim para todo mundo por alguns motivos bem óbvios:

  • Visitantes, do nada, entrariam em sites que funcionavam e não funcionam mais
  • Donos de websites teriam seus sites perfeitamente funcionais sendo quebrados do dia para a noite, sem que eles tocassem em nada.
  • Desenvolvedores de browsers perderiam mercado, pois o público trocaria de software pois “tal site funciona em browser X e não em Y”

Então, vamos tentar manter todo mundo feliz!

Nunca vamos poder remover APIs ruins da Web?

Isso vai depender de alguns casos específicos, algumas APIs ruins já foram removidas da web, mas, somente ter de estudar e pensar como podemos remover e qual é o impacto que isso causa no usuário já é um processo bem complexo.

O que podemos fazer então?

Existem algumas alternativas que as pessoas pensaram para resolver o problema. A primeira delas é a mais obvia e simples possível: vamos atualizar o MooTools! Isso é uma boa ideia? Sim, mas não resolve nosso problema. Mesmo que o MooTools seja atualizado, os sites que o utilizam há anos, provavelmente, não vão atualizar sua cópia da biblioteca.

Outra solução proposta é forçar as pessoas a atualizarem sua cópia do MooTools. Mas, sendo realista, quantas vezes, nós, desenvolvedores, lemos as patch notes que saem de todas as bibliotecas de todos os projetos que usamos? E pior que isso, quantas vezes atualizamos essas bibliotecas?

Mesmo se alguém fosse capaz de identificar e entrar em contato com todos os sites afetados pela mudança no MooTools, dizendo para que todos os desenvolvedores destes sites atualizassem suas versões (que podem ter mais de oito anos de idade) do site somente, para resolver um problema de compatibilidade e, supondo que todos eles cooperassem, isso seria um esforço de, no mínimo, anos.

Outra coisa que temos que nos preocupar é que a maioria destes sites são antigos e não estão sendo ativamente mantidos. Isso pode ser por vários motivos; às vezes o site simplesmente não é mais uma prioridade, às vezes o dono simplesmente “não liga mais”. No melhor caso, mesmo se o mantenedor ainda estiver por ai, ele pode não ser um mega programador, mas sim alguém que queria simplesmente um pequeno site para resolver um pequeno problema. Esperar que as pessoas vão ser complacentes o suficiente para que tudo dê certo é, de certa forma, um pouco ingênuo da nossa parte.

No final, o que deu?

Durante a reunião de maio de 2018 do TC39, o (não oficial) Array.prototype.smoosh foi oficialmente alterado de Array.prototype.flatten para Array.prototype.flat. Simples, não?

O que aprendemos aqui?

Esse artigo parece ser meio sem sentido só contamos uma história. Mas qual é a lição de moral por aqui?
O que aprendemos é que a web é tão frágil quanto ela é robusta. O que temos e conhecemos hoje por Internet é feita por pessoas e para pessoas, ou seja, temos que ter um ambiente onde todos estão dispostos a cooperar e resolver os mesmos problemas juntos.

Se cada um fizesse o que achasse interessante para si, então, muito provavelmente, ainda estaríamos enviando cartas ao invés de e-mails, pois a Internet não seria nada mais do que um local privado para pequenos grupos de pessoas que concordassem sobre o que ela iria se tornar, deixando a grande maioria do público para fora, pois seria um lugar muito mutável e “inabitável”.

Você não acha que devemos, cada vez mais, nos preocupar em pensar um pouco juntos? Afinal, a web é um lugar bastante grande agora.

Este artigo teve uma grande contribuição do artigo publicado pelo Mathias Bynens. Quem quiser entender um pouco mais sobre o funcionamento do TC39, pode dar uma olhada por lá.