sexta-feira, 15 de fevereiro de 2008

Cada qual no seu lugar

Cada linguagem de programação tem a sua personalidade, e as práticas de programação devem ser adequadas a cada uma delas. Algumas linguagens terão personalidades mais semelhantes, outras mais distintas e quanto maior o distanciamento entre elas maior é a probabilidade da aproximação a um problema ser diferente.

Utilizar uma aproximação errada a um problema numa linguagem é em muito semelhante a atribuir a tarefa errada a um trabalhador. Não se coloca um programador de C a cortar HTML e não se coloca um web-designer a programar em C pois isso leva a insatisfação profissional, atrasos e erros na tarefa, enfim, um sem fim de consequências negativas.

Estruturado


A programação estruturada é aquela que mais "fora de moda" se encontra, apesar disso é a aquela que tem mais provas dadas. A grande maioria das aplicações que se vê por aí são feitas em C, praticamente tudo aquilo que utilizamos hoje em dia está assente em algo feito em C, como por exemplo muito do software que permite a existência da internet.

Dentro da programação estruturada há claro linguagens diferentes e as aproximações deverão também ser diferentes. Existem paradigmas distintos, tais como o imperativo e o funcional, existem as linguagens compiladas e interpretadas (conhecidas como linguagens de script).

Alguns do exemplos de linguagens estruturadas bastante populares são o C, o perl e o PHP. Logo aqui se nota a diferença entre as aproximações por linguagem. Não passa pela cabeça de ninguém resolver um problema da mesma maneira em PHP ou em C, a gestão de memória, o suporte para arrays dinâmicos, é tudo tão diferente que até sinto alguma dificuldade sequer em fazer uma comparação.

Objectos


Aqui entram algumas linguagens como por exemplo o python, o ruby , o Smalltalk e o IO language. Este é um paradigma mais geek e pouco utilizado, mesmo por alguns programadores de python e ruby que muitas vezes se desviam para a programação estruturada ou orientada a classes (muitas vezes chamada erradamente de orientada a objectos).

Aqui existem dois tipos de aproximação, o IO language, por exemplo, é prototipado, todos os objectos são criados clonando um objecto base e alterando depois o novo objecto. Nesta situação nem sequer existem classes.

Já em python por exemplo os objectos são criados a partir de classes, se bem que é possível criá-los copiando outros e depois alterando-os. Mas mesmo quando os objectos são criados a partir de classes na prática a diferença não é assim tão grande pois nas "new style classes" os objectos herdam todos implicitamente de um objecto base Object.

Neste tipo de linguagens a pureza de objectos da linguagem é um factor importante, pois é isso que permite a manipulação dos dados/código que caracterizam a aproximação ao problema utilizado normalmente neste tipo de linguagens. Closures e as funções e classes como first class citizens são também elementos importantes que condicionam a forma de utilizar a linguagem. É importante para quem não está familiarizado com o conceito de funções como first class citizens que isto não é um exemplo de passar funções como parâmetro ou devolver funções (as duas linhas são dois exemplos independentes):

func1(func2())
return func()


Este é o exemplo correcto:

func1(func2)
return func


No caso específico de python e ruby, quase tudo são objectos, mesmo as primitivas da linguagem, com excepção de uma mão cheia de keywords. No caso do ruby alguns operadores não são objectos (o == é mas o != não é, não sei bem porquê).

Classes


Neste tipo de linguagem inserem-se por exemplo o JAVA, o PHP e o C++, estes dois últimos só quando utilizados com classes em vez da forma estruturada. Este é actualmente o paradigma da moda. Regra geral as classes são utilizadas como contentores de funções que actuam sobre uma estrutura de dados comum. A diferença deste paradigma relativamente ao dos objectos é o facto de estas linguagens não serem puras de objectos, não terem closures nem funções e classes como first class citizens. A consequência disto é que a manipulação de código é muito reduzida e reflecte-se na aproximação ao problema.

Epilogo


Muitos dos programadores que experimentam uma linguagem com uma personalidade muito diferente daquilo a que estão habituados caem nos erros de fazer as coisas da forma errada para a linguagem que estão a usar. Por exemplo no How not to write Python code o autor enumera uma lista de coisas que ele considera que se deve ou não fazer em python.
Estranhamente o próprio autor cai num dos erros por ele referidos:
Python is Python, don’t try to emulate bad coding patterns from other languages

Ao fazer o seguinte:
A basic example: to check whether a function parameter is of a certain type, don’t use something like ‘arg.__class__ == MyClass’, use ‘isinstance(arg, MyClass)’.

Isto é claramente um erro pois o python é weak typed, não faz sentido o programador verificar tipos, pois isso é para linguagens strong typed e os compiladores/interpretadores serão com certeza mais eficientes a fazê-lo. No caso de haver de facto necessidade de verificar uma característica de um objecto, como por exemplo a existência de determinado método, vale mas verificar só mesmo essa característica. Ao fazer o que o autor diz no exemplo perdem-se as vantagens da pureza de objectos que permite fazer coisas como os file like objects. Este é o tipo de código típico de uma linguagem como o JAVA, mas... o próprio JAVA já faz isto pelo programador.

Outro exemplo que está bastante desviado da realidade do python é, ironicamente, o "Use Pythonesque coding pattern" que apresenta uma alternativa ao facto do python (tal como perl 5 por exemplo) não ter cases. Neste exemplo o erro é gritante porque o autor cria um código complicado para emular uma funcionalidade de outras linguagens, o facto de ele próprio falar nisto como uma alternativa ao case que não existe em python mostra o erro de estar a utilizar paradigmas de outras linguagens. A verdade é que nos últimos anos tenho programado muito em perl e python e nunca o meu código chegou a um ponto onde precisasse de um case.
O que me faz pensar que se estamos a tentar emular algo que não existe na linguagem... então estamos no caminho errado.

Sem comentários: