quinta-feira, 17 de dezembro de 2009

Programação Funcional?


Como começar um artigo sobre a compreensão do paradigma funcional? Primeiramente é necessário entender porque eu quero deixar de ser um deus para ser um reles titã. Sim, porque programar em modelos imperativos me tornam um deus: faça isto, faça aquilo... E os erros, ah os erros, eles vêm depois. No momento, somos apenas eu e a máquina a me obedecer.
Entendamos por este exemplo:


declare mundo boolean
declare trevas boolean
declare luz boolean


inicio


trevas = true

escreva(“Criar Luz?”)
leia(luz)

se(luz == true) faça
trevas = false
mundo = true
escreval(“Houve luz”)
escreval(“O Mundo foi Criado”)
senão
escreval(“Só existe trevas”)
fimdose


fim


Já deu para perceber quem é que manda aqui, não é? Eu disse haja luz e houve, e o mundo foi criado, e as trevas deixaram de existir. Se não der “pau” no programa, claro. Isto é linguagem imperativa: faça isto, faça aquilo. Sou eu quem determina o que deve ser feito, sou o próprio manda-chuva, senhor de meu universo. Tenho o poder de dar vida ao criar uma variável, interfiro em seu destino simplesmente alterando o seu valor, e posso gerar consequências, caso queira, definindo estruturas de decisão. E o melhor, posso dar fim em tudo, afinal, eu sou deus.
Como pode-se perceber, neste meu “creation of the world”, como um bom deus, indiquei sequencialmente minhas ordens de como fazer. O que haveria de ser feito dependeria de um fator variante, ou seja, daquilo que recebi como entrada. Se eu lesse false em minha entrada de luz, de acordo com minha condição, as trevas permaneceriam, e se a entrada fosse banana, não haveria cosmos, e sim caos, pois não fiz um tratamento de exceção.
Quando falamos em um modelo imperativo de programação, estamos entendendo (compreendendo?), como algo muito próximo de nossa linguagem. E isto está correto. Lembram dos verbos no modo imperativo?


Fazei, vós!


Graças a um outro deus , Von Neumann, de forma imperativa utilizamos um modelo matemático que busca processar uma única instrução de cada vez, procurando manter a integridade dos dados executados. Nem é preciso dizer que este paradigma é dominante que está estabelecido.
Aliás, o que vem a ser paradigma?
De acordo com o Priberam,


paradigma
(grego
parádeigma, -atos)
s. m.
1. Algo que serve de exemplo geral ou de modelo. = padrão
2. Gram. Conjunto das formas que servem de modelo de derivação ou de flexão. = padrão
3. Ling. Conjunto dos termos ou elementos que podem ocorrer na mesma posição ou contexto de uma estrutura.


Bom, podemos entender como ponto de vista. Porque não? É bem mais simples do que parece. Os paradigmas: orientado a objetos, a funções, a exceções, etc, nada mais são do que pontos de vista sobre melhores formas de se programar em dadas situações, determinados problemas. Ao menos não deveriam ser: um único paradigma, a solução de todos os problemas. Isto tem um nome que não me lembro. Ah, sim, isto é fundamentalismo.
Bom, no ponto de vista utilizado na interpretação da minha criação do mundo, como disse, é claramente definido o como fazer, e as nuances derivadas são diretamente ligadas ao o que fazer. Isto pode ser uma desvantagem, uma vez que o como fazer não garante o que deve ser feito e nem se foi feito. Estas garantias estão ligadas a mais comandos, mais códigos.
Mas apesar de tudo, ainda sou um deus. E se olhar mais à diante, posso criar santos a que chamarei de classes, que intercederão a mim. Sim, estou falando de orientação a objetos, que em suma, seria uma forma de organizar o modo de como fazer. Ou seja, um nível de organização e não um paradigma de fato. Algo como:


Inicio campeonato
     se(Vitoria(Galo) ==true) faça
         escreva(“Galo campeão!!!”)
     fimdose
fim campeonato


Onde:


Vitoria(time) {
     ganha = true
}



Que executaria:


Inicio campeonato
   se(
     Vitoria(time) {
       ganha = true
     }


==true) faça
   escreva(“Galo campeão!!!”)
fimdose
fim campeonato




Embora eu tenha sido o deus do como fazer, botaram o Flamengo no “o que fazer” e o meu galo ficou a deus dará. E mesmo sendo um deus, me frustrei. Isto por que, por mais que possa controlar o destino de uma variável, alterando o seu valor, ela ainda será uma variável, variante...
Depois de uma breve e singela pincelada sobre paradigmas e linguagem imperativa, vamos passar à orientação funcional, onde passamos a ser titãs, o melhores amigos, também deuses, do homem.
Vamos à nossa teogonia!
Como esta analogia poderia ficar extensa com a infinidades de titãs gregos, brinquemos apenas com Prometeu, um dos titãs mais conhecidos. O que o cara fez, e o porque do castigo? Se ele foi castigado, eu deixando de ser deus também serei? Estou saindo do contexto, voltemos.
Prometeu, segundo uma das lendas, foi dado a ele e seu irmão Epimeteu a tarefa de criar os homens e todos os animais. Epimeteu encarregou-se da obra e Prometeu encarregou-se de supervisioná-la. Na obra, Epimeteu atribuiu a cada animal os dons variados de coragem, força, rapidez, sagacidade; asas a um, garras outro, uma carapaça protegendo um terceiro, etc. Porém, quando chegou a vez do homem, formou-o do barro. Mas como Epimeteu gastara todos os recursos nos outros animais, recorreu a seu irmão Prometeu. Este então roubou o fogo dos deuses e o deu aos homens. Isto assegurou a superioridade dos homens sobre os outros animais. Todavia o fogo era exclusivo dos deuses. Como castigo a Prometeu, Zeus ordenou a Hefesto que o acorrentasse no cume do monte Cáucaso, onde todos os dias uma águia (ou corvo) dilacerava seu fígado que, todos os dias, regenerava-se. Esse castigo devia durar 30.000 anos.
Reparem a utilização do verbo atribuir, ou o que fazer. Tentando traduzir:


function atribuirdom(bicho,[dons]){
       bicho → dom + atribuidom(dons - 1)
}


Por questões obvias, esta expressão não pertence a nenhuma linguagem de programação específica. Esta função atribui um do ao bicho, recursivamente, de acordo com a quantidade de dons que foram disponibilizados. O fato é: foi claramente indicado o que fazer, que no caso seria atribuir dom ou dons a um bicho qualquer.
Isto é o paradigma funcional, um tratamento por avaliação de funções matemáticas. As funções podem ou não possuir parâmetros, assim como podem ser parâmetros de outras funções. A definição da função descreve de que forma ela será avaliada por outra função. Por exemplo, Epimeteu tem a função de f{atribuirdons, sob a função f{supervisão de Prometeu.
Como a função f{supervisão retornou algo estranho à função f{ordem, de Zeus, este procedeu a função f{dilarecafigado.
Brincadeiras à parte, o modo de compreensão à orientação funcional não é diferente do modo que funciona nosso pensamento. É bem provável que pouca gente pensa orientado a objetos. É mais fácil pensar orientado a função, uma vez agir (o que fazer), nos é sempre mais natural que pensar(como fazer).
Não tenho a intenção de ensinar esta ou aquela linguagem funcional. No momento, estou me dedicando a erlang. Este esboço foi originado na tentativa de compreender os princípios de uma linguagem. O mais importante e compreender onde e quando utilizar. Parafraseando o pessoal da 37signals, se uma linguagem não for boa para você, não a use. Vou continuar postando minhas análises de compreensão dos paradigmas, especificamente o funcional. Para terminar, alguns paradigmas a não seguir:



  • POG - Programação Orientada a Gambiarra


  • POF - Programação Orientada a Falácias


  • POMEE - Programação Orientada ao Menor Esforço Educacional.



Sugiram outras a não se seguir.



quinta-feira, 10 de dezembro de 2009

Abstração

O problema não podia estar mais claro. Era algo tão trivial, cotidiano. Fomos logo codificar. Afinal, porque perder tempo analisando algo que se vive no dia a dia? Era melhor aplicar a força intelectual na resolução.
Mas a análise não seria a premissa da solução? Bem, é sabido que todos analisam tudo antes de codificar (?), mas será que esta verdade se repete quando o problema é trivial? Quando desconhecemos o problema, tomamos um de dois caminhos: ou estudamos ou encontramos “o cara” que sabe das coisas, o analista de negócios, de processos, o expert no assunto. Tempo para nós mesmos decifrarmos a correção? Difícil...
Mas o caso era outro: era muito simples.
De imediato, surge a abstração, a forma com que esperamos levar a solução/problema para a máquina e virtualizar a resposta. Pensamos em objetos, nos orientamos a soluções e por fim materializamos uma idéia na forma de códigos e telas. Tudo pronto, uma formulação matemática pode resumir todo um pensamento. No entanto, orientamos o nosso pensamento proporcionalmente a complexidade do fato.
Isto significa que imaginamos soluções simples para problemas simples.
Eureka! Isto é maravilhoso.
Mas como julgamos a simplicidade de um problema? É possível imaginar uma solução simples para um problema que acaso imaginamos ser simples. Mas se o problema não era tão simples assim, nossa solução poderá ser falha, ou seja, haverá re-trabalho.
Vamos partir do ponto que, o bom seria soluções simples para qualquer tipo de problema. Pronto, desta forma nos orientamos que a resolução deverá caminhar para uma única estrada. Agora vamos nos ater ao julgamento do problema.
Sem querer pensar em Hume ou Descartes, o caso é que, constantemente, alternamos nossas decisões com base em conhecimentos adquiridos, ora empiricamente, ora racionalmente. Nossas decisões oriundas do pensamento racional, são aquelas em que agimos segundo um conceito adquirido num processo de busca intencional. Por exemplo, deverei usar o lambda para resolver isto porque, num dado momento, me houve uma necessidade parecida, precisei aprender lambda e, a partir daí, julgo ou sei os casos em que posso utiliza-lo. Este julgamento depende do quanto aprendi sobre lambda.
Quando algo é natural ao meu desenvolvimento, sem custo de aprendizado, julgo sua utilização conforme meu aprendizado ambiental, não necessariamente advindo de um fator externo ao meu meio.
Semana passada, no Coding Dojo Niteroi, foi levada uma proposta de problema bastante interessante: o caso do mijão. O problema era, quando um homem adentrasse em em banheiro público, ele procuraria o banheiro mais distante da porta; o próximo cara, ficaria o mais distante deste homem; e o próximo, so utilizaria um mictório caso houvesse a distancia de pelo menos um mictório entre os caras da direita e da esquerda.
Simples assim.
Pelo menos eu faço isto todo os dias, chego e se o banheiro está vazio, vou logo para o mais longe da porta. Onde trabalho tem três mictórios. Se já tem alguém, vou para longe dele, respeitando a “zona de respingo”. Se tem alguém no meio, espero.
É muito natural, tanto que eu nem saberia explicar quem ou mesmo se alguém me ensinou. É parte do meu meio de criação.
Assim, fomos direto ao código e, pessoalmente preocupei-me mais com a Linguagem de Programação aplicada do que com o problema em si. Intimamente já sabia que o primeiro ia para o ultimo, o segundo para o primeiro e o terceiro esperava. Sim, eu sabia. Dificuldade mesmo era na declaração de um array específico que quería-mos usar.
Até que uma boa alma pergunta: e se forem quatro ou cinco mictórios?
É preciso confessar que somente neste instante me deparei que o trivial não era tão simples quanto se mostrava. Divaguei em minhas reflexos, conversei com meus botões, tentei formulações matemáticas, e apenas descobri que um problema é sempre um problema. Nem difícil e nem fácil, apenas problema.
Em meus pensamentos pude notar um certo remorso, pois fui logo pensando em algumas soluções antigas que utilizei no passado, o quanto eu desrespeitei o problema em si. Sim, foi mesmo desrespeito e, claro, tive re-trabalho.
Sempre acreditei que, nestes casos, a análise era incompleta, mas na verdade era falha mesmo. O problema foi considerado simples e a análise foi apenas simples. Logo, no lugar de uma solução simples, produzi algo imaturo, que nem era solução.
Como julgar se o problema é simples ou complexo? Não julgue, problemas são problemas. Aliás, em termos de programação, não julgue nunca, tenha sempre certeza. E isto possível? Bom, encerro com uma outra questão: acaso duvida que 2 + 2 = 4?