Muito se comenta nos meios de comunicação especializados em desenvolvimento de software presentes na internet como blog, fórum, site, etc. sobre linguagens de programação dinâmicas ou simplesmente linguagens de script. Alguns falam que a linguagem “A” é melhor que a “B”, que a “C” é mais rápida que a “B”, que ambas são apenas “fogo de palha” e que não vingarão, etc. Mas quase ninguém se pergunta qual o verdadeiro propósito desse tipo de linguagem? Quando devo utilizá-la e quando não devo? O que tem de diferente em relação às linguagens tradicionais? Etc.

Nos próximos parágrafos quero compartilhar com vocês o que sei sobre esse novo tipo de linguagem de programação, que não chega a ser tão recente assim na vida dos desenvolvedores, e que agora ganhou destaque por conta dos frameworks de desenvolvimento ágeis como Ruby on Rails, Grails, etc.

Começarei comentando sobre o protocolo MOP (Meta Object Protocol) que a maioria das LPDs implementam no seu core, suas principais características e quais as vantagens e desvantagens de utilizá-las em seu projeto.

Meta Object Protocol (MOP)

O que diferencia uma linguagem (orientada a objeto) dinâmica de uma não dinâmica são 2 aspectos fundamentais: Introspecção e Interseção.

  • Introspecção ou Reflexão: É a habilidade que uma linguagem tem de fazer com que seus objetos ou classes consigam ler seus atributos. É esse aspecto que nos permite saber quais atributos ou métodos fazem parte de uma determinada classe.
  • Interseção: É a habilidade que uma linguagem tem de poder alterar o comportamento de um determinado objeto em tempo de execução. É esse aspecto que nos permite fazer um binding de um método para um objeto ou classe.

O protocolo MOP introduz em uma linguagem de programação o suporte a introspecção e interseção através do armazenamento do estado de seus objetos ou classes em meta-objeto ou meta-classe associados a cada uma das entidades em questão.

Algumas linguagens implementam o MOP parcialmente, como é o caso do Java que é puramente introspectiva para dar suporte a API Reflection, outras linguagens implementam por completo como é o caso do Python, Scala, Ruby, Smalltalk e Groovy.

Os recursos do MOP permitem que as novas linguagens de programação possam agregar funcionalidades extras é mais flexíveis às funcionalidades já presentes nas linguagens de programação tradicionais, é o caso da programação orientada a aspecto (AOP) que só é possível em linguagens de programação que implementam esse protocolo.

As principais características das LPDs

A seguir, relaciono as principais características das LPDs que diz respeito, principalmente, à simplicidade de codificação e às funcionalidades alternativas em relação às linguagens de programação tradicionais orientada a objeto.

Tipagem Dinâmica

A tipagem dinâmica consiste em preservar o desenvolvedor de especificar o tipo de dados de uma variável, atributo ou retorno de método. Esse recurso nem sempre é eficiente, dependendo do problema em questão, pois os tipos de dados são resolvidos em tempo de execução podendo, inclusive, ocasionar erros de parse. Tipagem dinâmica no Ruby:

local = "Variável Local"
@estatico = "Variável de Instância "

Closures

Closure são dados que referenciam uma função e podem acessar variáveis locais e não locais que estejam no escopo externo a sua invocação. Closure no JavaScript:

var funcaoClosure = function (msg) {
	alert(msg);
};

function executaClosure(closure) {
	closure(“Mensagem parâmetro de um closure”);
}

executaClosure(funcaoClosure);

Sobrecarga de Operador (Operator Overloading)

É um caso específico de polimorfismos onde o desenvolvedor pode modificar o comportamento padrão de um determinado operador da linguagem. Sobrecarga de Operador no Groovy:

class A {
	def value

	public A(value) {
		this.value = value
	}

	def plus(b) {
		return “soma de ${value} com ${b.value}”
	}
}

println new A(“B”) + new B(“B”) // imprime: soma de A com B

Sintaxe nativa para array e map

As LPDs possuem uma sintaxe própria para array e map. Dessa forma não é necessário usar operadores para criar esses elementos deixando a desenvolvimento ainda mais rápido é intuitivo. Sintaxe nativa para array e map no Smalltalk:

#(1 2 3 4 5)
#(um:1 dois:2 tres:3 quatro:4 cinco:5)

Métodos helpers para coleções

As LPDs possuem métodos utilitários que auxiliam o desenvolvedor nas operações comuns realizadas nessas estruturas de dados. Alguns dos métodos helpers mais comuns são:

  • each - Permite iterar sobre uma coleção com um closure.
  • find - Encontra a primeira ocorrência definida em um closure.
  • findAll - Encontra todas as ocorrências definida em um closure.
  • collect - Para cada elemento da coleção executa o closure e o resultado retorna em uma nova coleção.
  • join - Concatena o valor dos itens da coleção com uma string.

Métodos Helpers no Groovy

[1, 2, 3].each { 
	item -> print "${item}-" 
}

def value = [1, 2, 3].collect { 
	it * 2 
} 
// retorna 2

def value = [1, 2, 3].find { 
	it > 1 
}
// retorna 2

def value = [1, 2, 3].findAll { 
	it > 1 
}
// returna 2 e 3

Operador de navegação segura

Muitas LPDs disponibilizam um operador para evitar erro de nullable object. Operador de navegação segura em Groovy:

def value = object?.name

Expressão dentro de String

Para facilitar a exibição ou concatenação de informações em uma string muitas LPDs permitem embutir expressões dentro de uma string. Expressão dentro de String no Groovy:

def email = “email@email.com.br <mailto:email@email.com.br>”
println “EMAIL: ${email}”

Eval

Toda LPD apresenta um recurso para resolver expressões em tempo de execução. Esse recurso é o mais importante, o que dá sentido a dinâmica da linguagem de programação além de aumentar a flexibilidade. Geralmente chama-se Eval a classe ou o método com esse recurso. Eval em Groovy:

println Eval.me('2 * 4 + 2')
// retorno 10

Vantagens das LPDs

A maioria das vantagens desse tipo de linguagem é a simplicidade de codificação e as funcionalidades alternativas aos recursos já oferecidos nas linguagens de programação (orientada a objeto) tradicionais. Dentre essas vantagens podemos destacar:

  • Clareza na sintaxe da linguagem: Vários recursos disponíveis nas LPDs reduzem a quantidade de código e simplificam a sintaxe, dentre eles podemos destacar a tipagem dinâmica onde a declaração de uma variável se resume apenas ao seu nome e valor.
  • Facilidade em trabalhar com coleções: Com os métodos helper e closure as operações sobre coleções se tornam muito mais amigáveis.
  • Flexibilidade em poder executar expressões em tempo de execução: Recurso muito importante e útil no desenvolvimento, pois você não fica amarado em ter que compilar sempre o código caso exista uma mudança de expressões, ou ainda, você pode executar códigos dinâmicos que podem, inclusive, estar armazenados em bandos de dados como fórmulas para cálculos, etc.
  • Expansão das funcionalidades da linguagem: Através do recurso de sobrecarga de operadores, você pode mudar completamente o comportamento de uma classe.
  • Invocação dinâmica de métodos com closure: Você pode chamar um método de forma dinâmica mapeando a chamada para um closure, recurso esse que os framework de desenvolvimento ágeis utilizam a vontade, como é o caso do Grails que possui métodos dinâmicos como findBy* (qualquer atributo da classe) e findAll*(qualquer atributo da classe) para permitir consultas no banco de dados através da API Hibernate.
  • Novos conceitos e paradigmas: Com os recursos das LPDs novos conceitos e paradigmas de desenvolvimento de sistema surgiram ou foram possíveis de implementação como o caso da programação por convenção, programação orientada a aspecto, etc.

Desvantagens das LPDs

Sem dúvida as desvantagens das LPDs são poucas, uma vez que elas possuem as funcionalidades tradicionais de toda linguagem de programação, além de possuir as características específicas de uma linguagem de programação dinâmica. Confesso que foi difícil achar desvantagens relacionado à sua estrutura como linguagem de programação, foi mais fácil achar desvantagens fora desse escopo.

  • Mudança de Sintaxe - Muitos recursos das LPDs apresentam um novo padrão de sintaxe, que pode ser uma dificuldade para os novos usuários, mas que com o tempo pode se acostumar sem nenhum problema.
  • Novos paradigmas - Com a popularidade desse tipo de linguagem de programação novos conceitos e paradigmas foram surgindo e com eles a dificuldade de adaptação e aceitação para aqueles tipos de usuário mais apegados as linguagens tradicionais.
  • Maior custo de processamento - Com os recursos dinâmicos como tipagem dinâmica, parse em tempo de execução, closure, etc. e óbvio que o custo de processo tende a aumentar em relação às linguagens de programação tradicionais, mas são pontos que se equiparam em detrimento aos benefícios que esses recursos traz para os desenvolvedores, além de não ser um ponto crítico por conta da evolução dos hardwares nos últimos anos.

Conclusão

Assim como no paradigma de programação orientada a objeto, entenda as linguagens dinâmicas como uma herança das linguagens de programação tradicionais, elas não estão ai para substituir as funcionalidades que todo desenvolvedor sabe, mas sim proporcionar funcionalidades alternativas para que o trabalho de desenvolvimento de sistema possa ser feito de maneira flexível, simples, claro e objetivo. As LPDs estão ai, ganhando cada vez mais mercado, e não são mais promessas, mas sim realidade e não podem passar despercebidas, pois os ventos estão levando para esse lado e pode ser que num futuro bem próximo se torne o que a programação tradicional é hoje. O Java 8 vem ai, pesquise e veja quais as novas funcionalidades dessa versão, qualquer semelhança não será mera coincidência.

Referências:
  • Gregor Kiczales, Jim des Rivieres, Daniel G. Bobrow (1991) - The Art of the Metaobject Protocol.
  • Groovy - Groovy Quick Start. Acessado em: 05/04/2011.
  • Dynamic Programming Language - Wikipedia. Acessado em: 05/04/2011.
  • Groovy (Programming Language) - Wikipedia. Acessado em: 05/04/2011.
  • Smalltalk - Wikipedia. Acessado em: 05/04/2011.
  • Python (Programming Language) - Wikipedia. Acessado em: 05/04/2011.