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…)