Linguagens são frameworks

por mvallebr.

Está na moda criarem linguagens novas para tudo hoje em dia. Para qualquer coisa, você acha uma DSL (Domain Specific Language) própria para aquela tarefa. Acho ótimo o fato de o mercado de trabalho de TI estar exigindo que os profissionais sejam mais que "programadores de uma linguagem" e acredito que muitas vezes uma linguagem específica para um fim pode ser uma boa idéia, mas... será que não está havendo um exagero?

Um excelente exemplo relacionado com o caso é o lançamento da linguagem Coffee Script, um wrapper para a linguagem Javascript desenvolvido pela equipe do Rails. A justificativa para a criação da linguagem foi o fato de Javascript ter diferenças de API em cada versão de browser. A linguagem foi muito criticada, justamente por ser totalmente desnecessária. Por que usar uma nova linguagem para algo que pode ser alcançado facilmente com um framework? Por que não criar uma API Javascript portável que mascare as diferenças dos browsers?

Essa questão e o email do Linus que eu citei em outro post me fizeram questionar o seguinte: o que faz você precisar de uma linguagem nova ao invés de um framework novo para a mesma linguagem? Quais são as vantagens de uma linguagem de programação, comparando com as vantagens de um framework? Você realmente precisa utilizar uma nova linguagem?

A questão pode parecer óbvia a princípio, mas não é! Mesmo quando há mudanças de paradigmas entre uma linguagem e outra, já foi provado que é possível substituir uma nova linguagem por um bom framework. É o caso de C e C++. Veja a biblioteca GObject, feita pelo pessoal do GNOME, e vai entender o que estou dizendo! Eu considero esse framework e a idéia do mesmo uma verdadeira obra de arte no mundo da programação.

Com essa ideia na cabeça, listo abaixo uma lista de vantagens que consigo reconhecer no uso de uma linguagem de mais alto nível, como ruby, scala, Java, Coffee Script, etc. ao invés de uma linguagem de mais baixo nível como C, pascal, etc.

  1. Inserção automática de código - Através de construções da própria linguagem, é possível que o compilador entenda quando deve realizar algumas tarefas sem que o programador explicitamente diga isso no programa. É o caso dos destrutores do C++, que são chamados automaticamente sempre que desalocamos um objeto da memória ou de tratamento de exceções em C++ e Java. O compilador da linguagem entende que esse comportamento é sempre desejável.
  2. Mascarar a complexidade para o desenvolvedor - É o caso, por exemplo, do Garbage Collector do Java, do foreach, de várias linguagens, etc. O feature poderia ser perfeitamente implementado pelo desenvolvedor em outra linguagem, mas o uso da linguagem de mais alto nível tira a obrigação do desenvolvedor de se preocupar com isso, levando essa responsabilidade para o compilador ou para a virtual machine / interpretador, se for o caso.
  3. Reuso de código - por exemplo, ao invés de implementar várias rotinas para lidar com números complexos em seu código, você pode utilizar Python, que já lida com números complexos nativamente. A linguagem já contem muita coisa pronta que pode ser utilizada sem que o desenvolvedor precise ficar implementando novamente em cada programa.
  4. Reflexão - É o caso de Reflection, no Java, ou de vááários features de linguagens dinâmicas como JavaScript, Ruby, etc. BEM grosso modo, podemos encarar reflexão como a capacidade de uma linguagem de gerar código e executá-lo a partir do próprio código. É o caso dos blocos em Ruby: você passa um bloco de código como argumento para uma lista e ela executa o mesmo para cada elemento da lista, por exemplo. Você pode construir uma string dinamicamente com um bloco de código e passar essa string como argumento para uma função, que executará esse código dentro da mesma diversas vezes para diferentes variáveis.

Provavelmente seria possível aumentar bem essa lista, mas creio que esses pontos acima resumem bem as vantagens de uma linguagem de mais alto nível. Mas o simples fato de a linguagem prover essas vantagens não é justificativa para o uso da mesma ao invés de um framework e quero defender esse meu argumento comparando cada ponto acima com a utilização de um framework. Vejamos:

  1. A inserção automática de código é realmente uma vantagem de linguagens de mais alto nível, mas pode ser implementado em linguagens de baixo nível sem problemas. Novamente, cito uma lib feita pelo pessoal do gnome. Veja a GError para ver como eles implementaram o esquema de exceções em C.
  2. Esse é o ponto em que mais sou contra o uso de linguagens ao invés de frameworks! Um recurso deve estar disponível para que você não tenha de reinventar a roda todas as vezes em que for utilizá-lo, mas de maneira nenhuma ele deve estar disponível com a intenção de tirar a necessidade de conhecimento do desenvolvedor sobre o assunto. No exemplo do garbage collector, digo que tudo bem usá-lo desde que seu uso seja consciente e você saiba o que está fazendo e o que está sendo gerado no código executável, exatamente. Usar um recursos desses para não precisar aprender sobre alocação de memória é horrível, indesejável e deveria ser crime! NADA substitui o conhecimento humano, não tente trocar seres humanos por linguagens! Outra coisa: se quiser usar um garbage collector, pode criar um framework em C que tenha esse comportamento, mascarando as funções malloc e free com suas próprias e deixando uma thread em execução, em background. Gostaria de saber se alguém consegue NÃO usar um em Java... 
  3.  Se a grande vantagem da linguagem é trazer recursos prontos, então é uma desvantagem utilizá-la. Crie uma biblioteca com essas funções na linguagem de mais baixo nível, não há a necessidade da criação de uma nova linguagem só por isso. No caso do exemplo de números complexos, legal python ter isso pronto, mas e se você quiser usar quaternions (números complexos de quarta dimensão)? É difícil herdar e estender comportamento nativo da linguagem (tente criar uma classe que herde da classe String do Java!), mas seria bem fácil fazê-lo com um framework. 
  4. Reflexão - Esse é o grande ponto forte de linguagens como Ruby e Javascript. Graças a esse feature, eu consigo desenvolver em Ruby on Rails sem precisar criar cada atributo de uma tabela em meu bean active record. Don't repeat yourself, they say! Escrever o mínimo de código é importante e um grande aliado à PRODUTIVIDADE, motivo pelo qual eu costumeiramente uso RoR. Mas não pense que isso não tem um preço: é comum ver programadores usando reflexão a torto e direito, onde normalmente não seria necessário. Reflexão tem um custo e é bem mais lento e menos otimizável do que escrever um código nativo e sem esse recurso que também cumpra a necessidade. Detalhe: você precisaria implementar um compilador, chamar um ou usar uma "lib de compilação" se quiser emular esse recurso em uma linguagem como C, mas... é possível! 

Olhando para os pontos acima, pergunte-se: é realmente necessário usar uma linguagem de mais alto nível? Se você pode evitar o uso, evite! É muito preferível utilizar um framework a utilizar uma linguagem, a não ser que se tenha um BOM, um EXCELENTE motivo para fazê-lo. No caso do Ruby on Rails, por exemplo, eu tenho um ótimo motivo: não existe o framework para fazer o que eu faço em RoR em C. Mas pode ter certeza do seguinte: se não tiver vantagem nenhuma em utilizar uma linguagem de mais alto nível, seu uso é uma grande desvantagem. Evite!!