A linguagem CoffeeScript existe desde meados de 2009, e evoluiu bastante desde então, mas o destaque que vem recebendo nos últimos meses é sem precedentes. Tem havido várias discussões acaloradas em torno da linguagem, em grande parte devido a controvérsias em volta da decisão de tornar CoffeeScript a linguagem padrão para scripts no futuro Ruby on Rails 3.1, no lugar do JavaScript, o padrão anterior. Embora a mudança necessária para continuar usando JavaScript no RoR seja pequena, muitos desenvolvedores entraram numa polêmica que evoluiu para algo mais profundo, relacionado a linguagens em geral e ao seu papel no desenvolvimento web.
Os principais motivos da discussão da utilidade do CoffeeScript envolvem as vantagens técnicas do seu uso e se o ganho é apenas estético. É levantada também a preocupação com o esforço de aprendizado de mais uma linguagem. É importante, entretanto, entender as motivações do uso da linguagem para que se possa tirar uma conclusão mais objetiva.
Origens e Motivação
Para falar de CoffeeScript, precisamos começar com a razão de sua criação. O CS foi criado, segundo seus desenvolvedores, devido ao excesso de verbosidade, além da falta de “elegância” do JavaScript e de outras linguagens hoje usadas para o desenvolvimento na web.
Como sabemos, o JavaScript se tornou famoso pelo extenso uso em projetos web e, após a Web 2.0 a linguagem se tornou fundamental. Mas apesar de seu alto grau de adoção, a linguagem em si pouco evoluiu. O progresso ocorreu principalmente em volta da linguagem, com a criação de frameworks como jQuery, Prototype e muitos outros. O JavaScript já mostra efeitos da sua idade, se comparado com linguagens com paradigmas mais modernos como Groovy, Python e Ruby (embora algumas destas tenham sido lançadas antes mesmo do JS).
Contudo, com o suporte de JavaScript nos principais browsers e a quantidade de aplicações "legadas", uma atualização da linguagem quebrando a compatibilidade é algo que certamente não vai acontecer tão cedo. É neste ponto que entra o CoffeeScript.
Em vez de se posicionar como um substituto completo ao JavaScript, o CoffeeScript é construído sobre o JavaScript. Programas em CS são "compilados" para JavaScript através de um pré-processador. Os pré-processadores já são usados há décadas, por exemplo em C, com seus #ifdef e #define. Mas a técnica evoluiu muito desde então, sedo utilizada em vários outros contextos, como no framework GWT, em que o código escrito em Java é transformado em JavaScript e HTML; e no SASS, no qual uma "compilação" gera código CSS.
Sintaxe e formas de uso
A extensão utilizada em arquivos CoffeeScript é .coffee. Os arquivos são compilados para arquivos .js, sendo usados scripts para a compilação. Há instruções específicas no site do CoffeeScript, para instalação do compilador e outras ferramentas. Há ainda extensões que permitem a editores de texto reconhecer a sintaxe e indentação, como vim e gedit.
A seguir, faremos um mergulho na sintaxe da linguagem, por meio de trechos de códigos que destacam as principais diferenças (e semelhanças) entre CS e JS. O leitor notará que o CoffeeScript recebeu inspiração de várias linguagens, especialmente de Ruby e Python.
É possível testar trechos de código em CS no próprio site da linguagem, onde é oferecido um interpretador online acessível através do botão "Try CoffeeScript". Além das saídas dos códigos (se houver), você verá o código JavaScript gerado.
Os exemplos a seguir (baseados em códigos no site do CoffeeScript), começam com o obrigatório "Hello World"; a primeira coluna sempre mostrará o código em CS e a segunda, o código gerado em JavaScript:
|
CoffeeScript |
JavaScript |
alert "Olá Mundo!"
|
alert("Olá Mundo!");
|
Já no "Olá Mundo" vemos algumas diferenças em relação ao JavaScript, por exemplo a ausência do ponto-e-vírgula e os parênteses opcionais, característica também encontrada em Ruby.
Seguindo com os exemplos, ficarão mais evidentes outras características da linguagem, como a utilização do espaço significativo do Python; ou seja, não são necessários delimitadores de início e de final de blocos, com o contexto sendo demarcado através da indentação. Além disso, o if pode ser usado não só da maneira convencional, mas também no final das expressões, assim como em Ruby.
Atribuições
numero = 42
oposto = true
numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numeros[3..6] = [-3, -4, -5, -6]
|
var numero, oposto, numeros, _ref;
numero = 42;
oposto = true;
numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
[].splice.apply(numeros, [3, 4]
.concat(_ref = [-3, -4, -5, -6])),_ref;
|
Observe que não é necessário declarar as variáveis em CoffeeScript; além disso a facilidade para trabalhar com atribuições, inclusive em vetores, é útil em várias situações.
Objetos
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
|
var math;
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
|
Acima, veja como construções mais complexas, como a definição de objetos, ficam mais claras com a simplificação na declaração de funções e atributos.
Splat (parâmetros variáveis)
corrida = (vencedor, competidores...) ->
alert competidores
corrida "Jose", "Primeiro", "Segundo"
corrida "Joao", "Primeiro", "Segundo", "Terceiro",
"Quarto"
|
var corrida;
var __slice = Array.prototype.slice;
corrida = function() {
var competidores, vencedor;
vencedor = arguments[0],
competidores = 2 <= arguments.length
? __slice.call(arguments, 1)
: [];
return alert(competidores);
};
corrida("Jose", "Primeiro", "Segundo");
corrida("Joao", "Primeiro", "Segundo", "Terceiro",
"Quarto");
|
Nesse exemplo de utilização do operador splat, fica evidente o quanto de código é economizado e a clareza da construção.
Condicionais
alert "Eu sabia !" if elvis?
numero1 = 1
numero2 = 2
numero3 = 3
numero4 = -4 if oposto
numero5 = 5
|
var numero1, numero2, numero3, numero4,
numero5, numero6, numero7, numero8;
if (typeof elvis !== "undefined" &&
elvis !== null) {
alert("Eu sabia !");
}
numero1 = 1;
numero2 = 2;
numero3 = 3;
if (oposto) {
numero4 = -4;
}
numero5 = 5;
|
Em casos em que há muitas atribuições, o alinhamento das variáveis e seus valores aumenta a legibilidade. Além disso, se precisamos incluir uma condição isso pode poluir visualmente a sequência. Com a opção de condições no final da linha, esse desconforto é evitado.
Note que as melhorias não são somente estéticas. Por exemplo, no tratamento de existência da variável "elvis" acima, o código JS gerado já trata condições inesperadas para evitar erros em tempo de execução, especialmente erros comuns para usuários inexperientes em JS.
Comprehensions
cubes = (math.cube num for num in list)
|
var cubes, num;
cubes = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = list.length; _i < _len; _i++) {
num = list[_i];
_results.push(math.cube(num));
}
return _results;
})();
|
Estas sentenças "em uma linha" são úteis para operações que são executadas em coleções, simplificando o laço e o controle de condições.
Valores padrão para parâmetros
preencher = (recipiente, liquido = "café") ->
"Preenchendo o #{recipiente} com #{liquido}..."
|
var preencher;
preencher = function(recipiente, liquido) {
if (liquido == null) {
liquido = "café";
}
return "Preenchendo o " + recipiente +
" com " + liquido + "...";
};
|
Parâmetros opcionais e com valores padrão são úteis para evitar uma cascata de "overloads" muito comum em linguagens estaticamente tipadas. Em JavaScript os parâmetros são opcionais, mas muitas vezes é necesário percorrer a funcão para descobrir qual o valor padrão, caso determinado parâmetro seja omitido. Já com CoffeeScript, isso é explicito.
Interpolação
autor = "Wittgenstein"
citacao = "Uma foto é um fato. -- #{ autor }"
sentenca = "#{ 22 / 7 } é uma aproximação de π"
|
var autor, citacao, sentenca;
autor = "Wittgenstein";
citacao = "Uma foto é um fato. -- " + autor;
sentenca = "" + (22 / 7) + " é uma aproximação de π";
|
A interpolação é um recurso utilizado para deixar mais clara a concatenação de uma descrição fixa com uma variável. Essa característica da sintaxe do CoffeeScript foi emprestada do Ruby. O conteúdo dentro do operador #{} é avaliado e executado no caso de expressões mais complexas, gerando uma string com a substituição já feita.
Ligação (binding) de funções
Conta = (cliente, carrinho) ->
@cliente = cliente
@carrinho = carrinho
$('.shopping_cart').bind 'click', (event) =>
@cliente.efetuar_compra @carrinho
|
var Conta
var __bind = function(fn, me){
return function(){
return fn.apply(me, arguments);
};
};
Conta = function(cliente, carrinho) {
this.cliente = cliente;
this.carrinho = carrinho;
return $('.shopping_cart')
.bind('click',
__bind(function(event) {
return this.cliente.efetuar_compra(this.carrinho);
}, this));
};
|
A sintaxe para a criação de funções anônimas é muito mais legível, em comparação com o JS. Há duas variações. Com a seta simples, "->", o pré-processador apenas cria a função com os parâmetros determinados; com a seta dupla "=>" é enviado automaticamente o "this" (objeto com o qual a função está ligada).
Os atributos com prefixo @ são uma substituição ao "this", utilizados para que o escopo das variáveis seja apenas no contexto da função, ou seja enviado para outra função no caso de "callbacks". Outro recurso útil é a possibilidade de colocar valores padrão para os parâmetros, deixando bem clara a forma de utilização da função.
Como o código JavaScript gerado pelo tradutor do CoffeeScript é sempre "um-para-um", ou seja para cada instrução CoffeeScript temos uma correspondente em JavaScript, podemos utilizar funcionalidades de biblioteca de terceiros. No exemplo acima (que utiliza JQuery), o pré-processador do CS irá fazer a tradução de maneira correta para o JavaScript, preservando as funcionalidades que temos só no JQuery.
Vigiando o código
É bem comum, ao montar JavaScript, utilizar o arquivo .js separadamente da aplicação para facilitar os testes e ajustes finos, utilizando apenas um arquivo html estático com um trecho de código específico para isso. Com a opção "--watch" do compilador do CoffeeScript, é possível deixá-lo "vigiando" os arquivos .coffee de maneira que, assim que forem alterados, os arquivos .js correspondentes sejam criados evitando passos adicionais – como chamar o compilador manualmente – para esses testes separados da aplicação em arquivos html pontuais.
Conclusões
Examinando alguns exemplos da sintaxe do CoffeeScript, já se pode observar como a linguagem traz os mesmos recursos do JavaScript, de maneira bem mais elegante e clara, exatamente como esperado de linguagens mais modernas.
A legibilidade do código tem sido valorizada cada vez mais. Estudos conhecidos mostram que a manutenção do código representa mais tempo do que sua criação em seu ciclo de vida, e autores como Robert C. Martin (Uncle Bob) em seu famoso livro "Clean Code" mostram técnicas para manter o código limpo e legível. Uncle Bob argumenta que o desenvolvimento de software se compara ao trabalho de artesãos, e defende a preocupação em se criar código legível e compreensível por outros desenvolvedores, e não somente por quem o escreveu.
Além da elegância e clareza, há outros fatores que determinam o uso de uma nova linguagem. Muitos deles não quantificáveis, cabendo à avaliação da equipe, arquiteto ou empresa; outros envolvem preferências pessoais.
Você escolheria a linguagem CoffeeScript para um novo projeto?