2006/08/19

Programação (funcional) moderna.

Faz algum tempo comecei este post, mas não tinha tido a inspiração para terminar. O post anterior, sobre a comparação entre recursos de linguagens "funcionais" modernas e de linguagens orientadas a objeto serviu como estopim para terminar de escrever.

Para quem trabalha com programação usando variantes de ML e/ou Miranda, a fatídica pergunta "que linguagem(ns) você usa?" costuma ser um tiro no escuro. A resposta dificilmente é conhecida, na maioria dos casos: O'Caml e Haskell, mesmo sendo as mais conhecidas, são bem pouco utilizadas fora da academia.

Logo, você se depara com o clássico ponto de interrogação, que aparece no rosto da pessoa. Para esclarecer o que você está falando, a tentação inicial é classificá-las como linguagens funcionais, preferencialmente atribuindo também o adjetivo "modernas" - já mantendo no engatilhadas as respostas para quem diz "sim, sei, que nem LISP". Entretanto, classificar como uma linguagem funcional é muito comum: se você perguntar para qualquer programador de Haskell o que é a linguagem que ele usa, a resposta é essa. Alguns irão se extender um pouco mais, dizendo que é "fortemente tipada", e outros até vão falar em "lazy".

Entretanto, mesmo que o ponto principal de Haskell ou de O'Caml seja o paradigma funcional, o que faz delas linguagens flexíveis ou modulares não é o fato de serem funcionais, e sim a combinação dos recursos que elas oferecem. Tipagem forte e estática, tagged unions, pattern matching, inferência de tipos, type classes (no caso da segunda), funtores (no caso da primeira), tipagem algébrica, e outros tantos features que são igualmente importantes para a modularidade e aplicação real da linguagem.

Faz algum tempo que li um post não diretamente relacionado com o assunto em questão, mas que me levou a pensar sobre a sinergia nestas linguagens de programação, e enxergar as suas grandes vantagens como um resultado direto da combinação dos seus recursos. E no fundo, esta é a grande vantagem delas: a facilidade que o programador possui para se expressar através da combinação dos diversos recursos que elas oferecem.

Talvez esteja na hora de redefinir o adjetivo "moderno" na área das linguagens de programação, ou encontrar um novo para descrever esta gama de linguagens realmente modernas, que está anos-luz à frente das clássicas imperativas-OO.

E é isso.

2006/08/12

Pattern-matching e o visitor pattern

Trabalhando diariamente com C++, sendo praticamente um zero à esquerda em orientação a objeto e à engenharia de software da mesma (por opção), comecei a procurar um pouco mais sobre o paradigma, para encontrar recursos que espelhassem algumas funcionalidades que encontro em Objective Caml, especialmente uma: pattern-matching.

Depois de alguma procura, encontrei este artigo, que faz uma comparação direta entre C++, O'Caml, algumas implementações de SML, e outras linguagens, citando justamente o pattern-matching de O'Caml como uma de suas vantagens. Nesta comparação, traz-se a análoga ao pattern-matching no mundo OO, que em outro artigo descobri ser uma técnica de engenharia de software chamada visitor pattern.

Este outro artigo, do criador da linguagem Scala, vem em defesa do pattern matching, trazendo inclusive críticas que são feitas a este recurso, entre elas, que é desnecessário, que quebra o encapsulamento do código, e que não é extensível.

Depois de ler sobre estas informações, fui no artigo da Wikipédia e vi o exemplo da técnica, feito em linguagem Java. Percebi que as críticas que são feitas sobre pattern matching, na verdade, são críticas que muito bem poderiam ser feitas sobre o visitor pattern. Para começar, você precisa criar vários objetos só para tratar os casos possíveis. Um exemplo de 4 casos chega a dar dezenas de linhas de código, enquanto que em O'Caml, você tem uma linha para cada case, em exemplos simples com esse.

Outro ponto importante é a maneira que se utilizam as duas técnicas: no caso do pattern-matching, este é usado dentro de módulos, isolado do programa principal, ou até mesmo no próprio programa principal, mas como um meio de organizar dados obtidos por entrada e os dados de módulos: não há problema de flexilidade nem de extensibilidade, e a técnica se ajusta muito bem para este uso. Também é interessante notar que você não pode realmente usar um pattern no caso do visitor pattern: o compilador faz uma comparação apenas baseando-se no tipo dos objetos, e não é possível usar valores concretos: strings, caracteres e inteiros ficam de fora, o que já limita bastante a técnica em comparação com a primeira.

Por último, mas não menos importante: pelo fato de precisar de chamadas de métodos extra e criar uma dependência desnecessária entre classes, pode-se perceber que quem quebra o encapsulamento na verdade é o visitor pattern, que faz com que o programador precise de muito código só para conseguir um recurso simples. Como já disse o próprio criador da linguagem Scala, as críticas feitas ao pattern matching normalmente originam de quem não entendeu como funciona o recurso. O que faz muito sentido se o problema for observado do ponto de vista prático: os programadores que utilizam orientação a objetos, especialmente aqueles que só utilizam uma única linguagem monoparadigma, raramente conhecem o suficiente a programação funcional moderna para entendê-la, e perdem muitas das vantagens associadas a ela.

E é isso.