Clojure e simplicidade

AVISO – me empolguei um pouco nessa postagem, para uma introdução mais gentil, verifique o post após esse.

Qualquer linguagem baseada em LISP, como Clojure, tem o mesmo problema: as pessoas falam de como a linguagem é fantástica, como ela revoluciona como você programa, até o momento em que você resolve entender por que. Aí você estuda a linguagem, aprende uma ou outra coisa, e não entende porque as pessoas falam tão bem dela.

Esse ciclo se repete várias vezes, e várias vezes, e você nunca entende o motivo das pessoas falarem tão bem. Até um dia em que você finalmente entende – e é aí que você vira uma dessas pessoas que falam bem, mas ninguém mais entende por que.

Clojure é, basicamente, um LISP que roda sobre a JVM. Porém, diferente de common LISP, Clojure possui duck typing – LISP não. É essa foi a primeira realização – tipagem dinâmica não implica em duck typing.

Ruby, JS, e Clojure possuem métodos (ou keywords, ou funções) que rodam sobre qualquer tipo que atenda aquele protocolo. for element in array, por exemplo, roda em Ruby e JS da mesma maneira para Arrays, ou para Objects (em JS) ou Hash, Set, para Ruby. Em Ruby, é porque todos implementam o método .to_a. Já em Clojure, o nth serve para pegar um elemento de uma coleção qualquer, seja ela uma List ou Vector, usando (nth ["some" "elements"] 1). E como é isso em Common LISP? Bom, se for uma List, usa-se: (nth 1 '("some" "elements")). Se for um Vector, com (aref (vector "some" "elements") 1). E assim por diante (o que quer que isso signifique nessa situação, já que nem posicionamento dos parâmetros nem nome das funções é consistente).

A segunda coisa interessante de Clojure é a sua “sintaxe”, ou na verdade, ausência de sintaxe. Na prática, a sintaxe não existe – você programa definido diretamente as S-Expressions, como se fosse uma lista de comandos. Por exemplo:

; uma definição de uma função
(defn sum-ages [people]
  (reduce + (map :age people)))

; uma definição de uma lista
`(defn sum-ages [people]
   (reduce + (map :age people)))

A segunda expressão, apenas pela presença de um “quote”, torna-se uma lista. O primeiro e segundo elementos são Symbol, o terceiro elemento é um Vector que contém outro Symbol, e o quarto elemento é outra List: (reduce + (map :age people)), e assim as coisas continuam. Symbols, em Clojure, serão convertidos em sintaxe mais cedo ou mais tarde, então defn será clojure.core/defn, e chamará a função, símbolo, ou special-form desse nome mais cedo ou mais tarde. E isso é uma coisa fantástica pelos motivos que veremos a seguir. Mas o primeiro deles é bem óbvio: você não tem códigos – apenas dados. E como a linguagem é composta de dados, podemos manipulá-la, moldá-la, e alterá-la com macros. Além disso, Clojure é uma linguagem muito simples – ao contrário por exemplo, de Ruby, aonde a linguagem é complexa, mas programar nela é simples, em Clojure a linguagem é simples, mas programar nela é um pouco mais complicado.

E o motivo, por mais absurdo que pareça, é que nós, programadores, aprendemos a programar de forma errada
(more…)

Programação funcional, imutabilidade, e previsibilidade

O post de hoje é uma introdução à programação funcional, para podermos entrar finalmente em Clojure. Mas antes disso, vamos falar sobre como aprendemos a programar na faculdade, em cursos, e em todos os lugares. Vamos falar de “orientação a objetos”, principalmente, e vamos falar sobre “abstração”. A programação, como sabemos, é um exercício total de abstração – ao fazer um software, temos apenas um objetivo – fazer com que um trabalho, que provavelmente seria realizado de forma ineficaz ou manual, torne-se automático. Parece uma super-simplificação, mas é verdade. Processadores de texto substituem máquinas de escrever, editores de imagem automatizam vários trabalhos de restauradores, pintores, e desenhistas, e sistemas de folha de pagamento substituem o trabalho de vários matemáticos, contadores, etc. A profissão de todos continua válida – apenas simplificamos um pouco (ou MUITO!) o trabalho deles. E o nosso trabalho, de programadores, é simplificado com linguagens mais modernas, nas quais se escreve menos e se sub-entende mais. E para isso, precisamos aprender a escrever nessas linguagens. E aí entram os cursos, ou a faculdade.

Basicamente, aprendemos a programar nesses cursos, ou na faculdade, pensando em orientação a objetos. Para muita gente, essa é a única maneira sadia de se programar – afinal, orientação a objetos é o paradigma que representa melhor o mundo real, uma frase que muito se ouve. E essa frase é verdadeira, mas com uma pegadinha muito difícil de encontrar: o mundo real é um lugar complicado.

Esse será um post grande, portanto, está dividido em partes. Falaremos sobre a imprevisibilidade, depois mutabilidade e imutabilidade, e na última parte teremos exemplos em Ruby e Clojure sobre trabalhar com dados mutáveis e imutáveis.
(more…)

Clojure, LightTable, e uma nova forma de programar

Esses últimos meses tenho estudado Clojure, ClojureScript, e me entendendo com o ecosistema de tudo isso. Mas sobre a linguagem fica para outro post. Por hora, vamos a uma frase famosa: A language that doesn’t affect the way you think about programming, is not worth knowing, ou Uma linguagem que não afeta a forma que você pensa sobre programar, não vale a pena aprender. Essa frase, de Alan Perlis, mostra muita coisa do que eu penso antes de aprender uma nova linguagem, e vai explicar muito ao aprender sobre Clojure.

Antes de mais nada, vejamos como as linguagens evoluíram – C++ e Java são linguagens orientadas a objeto. Ruby, Python, e Scala também. Mas vejamos como usar uma lista em Java: abrimos o Java, numa IDE lenta como o Eclipse (que precisa de instalação, etc), importamos a lib…. qual lib mesmo? Bom, entramos num Javadoc, procurarmos a lib…. isso em C++ é pior ainda, já que em Java, pelo menos a IDE completa automaticamente os métodos (e algumas vezes, até os imports) pra nós.

Em Scala e Ruby? Abrimos o console, criamos uma lista, atribuímos a uma variável e digitamos: variable., seguido de alguns tabs e o console completa para a gente. Esse é o poder de um REPL (Read-Eval-Print-Loop, ou console, IRB, etc), e o REPL muda completamente a forma de escrever, explorar, entender e até mesmo de pensar em programação. Precisamos um dado no banco de dados? Entramos no REPL, digitamos User.create!(login: “foobar”), e voilá – temos um objeto criado. Não precisamos criar uma tela de cadastro para criar esse dado, não precisamos abrir o gerenciador do banco, etc.

Isso, com Clojure, é elevado ao limite.

(more…)