segunda-feira, 10 de novembro de 2014

O Zen na Programação

Já faz um tempo que leio um livro chamado "A Mente Liberta" de Takuan Soho, um dos principais tratados da filosofia Zen. Eu digo leio porque o simples ato de ter lido o texto em sua integridade não significa entendimento completo e outra leitura somada à vivência traz outro nível de entendimento. É um bom livro par qualquer um que deseja levar seu conhecimento a um novo nível e por isso trazer mais inovação.

Primeiramente:

  • Zen não é uma religião, embora tal disciplina tenha sido desenvolvida por monges budistas, é uma filosofia sobre as artes seculares, no livro acima falam mais da espada. 
  • Zen não é um sentimento ou estado mental
  • Zen é sobre aperfeiçoar sua arte, aperfeiçoando a sua ferramenta mais poderosa, a Mente
Eu escrevo sobre zen na programação porque programação é um assunto que eu domino. Algumas definições do livro:
  • O termo ignorância significa a ausência de Iluminação; isto é, a ilusão. 
  • Lugar de repouso significa o lugar onde a mente se fixa
Para mim a maior ilusão é a de achar que sabe tudo o que tinha que saber. Quem faz tal alegação está com mente parada e repousada na ignorância. A mente repousada na ignorância é incapaz de inovar pois ela está fixa e fechada.
E a grande verdade é que há sempre algo a se aprender e algo de novo para se produzir. Logo a mente para somente por ela mesma pois sempre haverá para onde se mover.

O que uma filosofia de mais 500 anos tem a ver com uma ciência de cerca de 60 anos de idade? Pois foi a mente humana que desenvolveu as artes descritas tanto nesse tratado quanto a ciência da computação.
E infelizmente as mentes dos desenvolvedores hoje estão presas em conceitos cristalizados conhecidos como padrões de projeto especialmente o GoF e Core Patterns. Igual o brinquedo abaixo, tentam encaixar os blocos pré-montados na caixa. Mas diferente da brincadeira, a forma da caixa muda a cada novo sistema, uma criança daria um jeito de encontrar novos blocos, mas muitos adultos da área de TI vão martelar os blocos e a caixa até que as peças mais ou menos se encaixem. Mais ou menos é um resultado sub-ótimo mas quase todos se contentaram com o mais-ou-menos, e por isso a mente deles está repousada na ignorância. De modo algum essa estagnação é por falta de vontade ou falta de inteligência mas sim por terem esquecido que as regras mudaram e que há outros blocos.

Caixa-encaixa

A diferença é que agora o cliente exige sistemas muito mais complexos do que esse modelo mal-fadado de caixa-encaixa pode permitir. E quando antes havia o consolo de que o próximo processador seria 2x mais rápido, hoje o programa deve também mudar sua estrutura para explorar o ganho de paralelismo. 
A criança do exemplo anterior iria se cansar do caixa-encaixa ingrato, e iria trocar de brinquedo. Este abaixo é mais interessante:



O Lego® com suas várias peças permite a criação de grandes obras. O Lego® permite que seus usuários expressem a sua criatividade encaixando as peças em várias combinações. No Lego uma única peça, pode se encaixar com outras desde que haja uma certa compatibilidade entre elas, embora pareça ser uma grande limitação, ainda assim é possível muitas combinações.

Igual às peças do lego, as funções da programação funcional podem ser combinadas e cumprir um propósito muito maior. Existem também restrições de compatibilidade, pois cada função espera um tipo de função, mas não chega a ser um modelo chave-fechadura igual ao caixa-encaixa da programação imperativa.

Moral da história é que a programação funcional trouxe novas fronteiras, algumas ainda inexploradas, e logo mais espaço para mente se mover. Um solo vasto para que a mente não mais repouse na ignorância.

sábado, 8 de novembro de 2014

Tratamento de nulos à moda Funcional

Criei no github um pequeno módulo JavaScript para implementar o conceito de tipo Optional. Opcional foi por sua vez inspirado no tipo monadico Maybe do Haskell. Monads são abstrações funcionais para tratar de computações indeterminísticas, ou seja com um resultado incerto mesmo dadas as mesmas entradas (p ex. consulta a um banco de dados, leitura de um arquivo). De fato todos os programas Haskell rodam em uma gigantesca monad IO que é a função main. A Monad que estamos implementando (faltam algumas funções para ser uma monad completa) que é a opcional, no caso trata de alguma atribuição que pode retornar valores nulos.

Modo tradicional de tratar nulos em Java:
void doSomethingWithX(X x){
     if(x != null){
         x.doSomething()
     }
}

NullPointerException é uma exceção extremamente comum em Java, isso porque além das tradicionais fontes de indeterminismo como as citadas acima, temos os próprios desenvolvedores que instanciam objetos mas não inicializam seus atributos e principalmente o padrão JavaBeans que permite o adiamento da inicialização, algo que não pode ser restringido agora dados os inúmeros frameworks que dependem de classes com construtores sem argumentos e métodos set públicos. Em linguagens como C/C++ a criação indiscriminada de objetos não inicializados é uma heresia e fonte dos erros mais nefastos que já existiram na computação mas em Java ou mesmo há o conforto dos coletores de lixo.

Mas no mundo funcional há um jeito mais elegante que é o tipo Opcional (Option em Scala, Optional em Java 8, Maybe em Haskell) que é um valor que pode ser nulo. O funcionamento é simples, há uma função Unidade, que recebe o valor (seja ele nulo ou não) se o valor for existente (não nulo) ela retorna o valor encapsulado em um objeto (chamado Some em Java 8/Haskell e Something em Scala, Unidade em Portugol). Daí a função de alta ordem conhecida como map recebe uma função de um único argumento com retorno, o argumento é do mesmo tipo do valor esperado no opcional e o retorno é um tipo arbitrário. No caso de um opcional vazio (Nothing em Scala, None em Haskell e EMPTY em Java) que representa um valor nulo o map é uma no-op, só retorna o mesmo opcional vazio e um opcional com valor retorna outro opcional que encapsula o valor retornado pela função-argumento de map (se esse valor for nulo é retornado um opcional vazio).

Agora, uma linguagem vastamente utilizada chamada Javascript acertou desde o começo no sentido de que tem funções de primeira classe, mas o tratamento de nulos ainda é feito do modo rudimentar, com if-else. Por isso eu criei o script opt.jsque faz exatamente o que eu falei neste longo testamento.

Contemplem o poder da programação funcional, todos aqueles if-else transfomaram-se em funções de ordem elevada, meta-funções, o desenvolvedor só precisa passar a função que representa a lógica desejada (lógica de negócios). As meta-funções encarregam-se da lógica de encanamento (pumbling), ou seja tudo aquilo que não é lógica de negócios e serve tão somente para manter o programa rodando.

Observem que:
  • opt é a função unidade
  • Os tipos Vazio e Unidade (carrega um valor não nulo) foram copiados do Haskell Some e None
  • Ainda falta a função/operação bind para que isto seja uma Monad completa

Programação Funcional - Minha História

I'm a Haskeller 
Já faz um tempo (mais de 4 anos) que eu não crio um post, praticamente desde quando eu entrei como desenvolvedor Java na minha atual empresa. No ano passado, enquanto eu trabalhava no então cliente do governo como outsourced, minhas bases imperativas começaram a se abalar ao prestar manutenção em seu código altamente entrópico. Havia várias boas práticas que poderiam ser aplicadas àquele sistema, módulo por módulo, ele poderia ficar melhor. Mas não demora muito para chegar alguém sem muita vontade de ler o código do outro e trazer mais caos ao sistema. Talvez seja a "melhoria" que não era tão boa ou a preguiça intelectual de alguns desenvolvedores, talvez seja uma especificação pobre, talvez seja uma péssima arquitetura, talvez um cliente ou a alta gerência que entubou frameworks horríveis. Esse é um jogo que ninguém tem toda a razão mas ninguém está de todo errado. Apontar o dedo é algo muito improdutivo e só cria inimigos amargos. Mas há um culpado muito esquecido chamado paradigma imperativo, baseado nas máquinas de Von Neumann. O que há de errado com as máquinas de Von Neumann? Basicamente nada, todos os computadores que existem no mundo hoje são implementações desse conceito, são máquinas de Von Neumann. E as linguagens de programação abstraíram esse conceito, na forma do que hoje é chamado de paradigma imperativo. E é a consequência do uso desse paradigma que trouxe efeitos nefastos. Ele permite a criação de estados mutáveis e compartilhados, basicamente os dados (variáveis) podem não ser locais a uma única função e por isso outra função (que pode estar sob responsabilidade de outro desenvolvedor ou do mesmo) pode modifica-los e mais outras funções podem depender deles. Parece uma bagunça não é? Mas é exatamente isso, um convite ao caos. Por mais que alguém tente organizar as coisas, o caos não é ilegal nesse sistema. A consequência é o indeterminismo, um sistema/componente que é alimentado com os mesmos dados e produz resultados diferentes. Determinismo em uma sociedade é algo ruim, mas na computação é o indeterminismo que é um grande passivo e seu custo aumentou muito com as arquiteturas paralelas e concorrentes. Durante esse tempo eu estudei Scala e li com mais profundidade sobre a programação funcional. Até então eu deixei-a de lado porque eu acabava revertendo ao modo imperativo que a linguagem Scala permitia, então eu acabava ficando com um Java bonitinho. Foi aí que eu descobri uma realidade maior 'a programação funcional é um paradigma, que vai ser cada vez mais adotado'. Do próprio criador de Scala e surfando na internet, eu encontrei o pináculo das linguagens funcionais, Haskell.  Sendo um linguagem puramente funcional, eu não podia reverter ao imperativo. Mas para quem trabalha em fábricas de software, ainda estamos presos (na maioria das vezes) à tecnologia que o cliente escolheu. O que acontece é que não são só os frameworks que estão obsoletos, é o paradigma vigente, a programação imperativa. E ao contrário do que se pensa, a programação funcional pode ser aplicada mesmo em linguagens consideradas não funcionais, inclusive pré Java 8. Nos próximos posts, mostrarei a revolução que a programação funcional traz às linguagens que não nasceram funcionais. Aconselho aprender Haskell, pois essa linguagem é a vanguarda da programação funcional, e mostra muitos conceitos de como superar as limitações Von Neumann. Enquanto isso, alguns artigos e palestras:
I'm a Haskeller