Por que Clojure tem 5 maneiras de definir uma classe em vez de apenas uma?

84

Clojure tem gen-class, reify, proxy e também deftype e defrecord para definir novos tipos de dados semelhantes a classes. Para uma linguagem que valoriza a simplicidade sintática e abomina a complexidade desnecessária, parece uma aberração. Alguém poderia explicar porque é assim? A defclass do estilo Common Lisp teria sido suficiente?

Salil
fonte

Respostas:

90

Esta é uma mistura de três fatores diferentes:

  1. O sistema de tipo específico do jvm
  2. A necessidade de semânticas ligeiramente diferentes para diferentes casos de uso ao definir tipos
  3. O fato de que alguns deles foram desenvolvidos mais cedo, e alguns mais tarde, conforme a linguagem evoluiu.

Então, primeiro, vamos considerar o que eles fazem. deftype e gen-class são semelhantes no sentido de que ambos definem uma classe nomeada para compilação antecipada. A classe Gen veio primeiro, seguida por deftype no clojure 1.2. Deftype é o preferido e tem melhores características de desempenho, mas é mais restritivo. Uma classe deftype pode estar em conformidade com uma interface, mas não pode herdar de outra classe.

Reify e proxy são usados ​​para criar dinamicamente uma instância de uma classe anônima em tempo de execução. Proxy veio primeiro, reify veio junto com deftype e defrecord no clojure 1.2. Reify é preferido, assim como deftype, onde a semântica não é muito restritiva.

Isso deixa a questão de por que tanto deftype quanto defrecord, uma vez que apareceram ao mesmo tempo e têm uma função semelhante. Para a maioria dos propósitos, queremos usar defrecord: ele tem todas as várias qualidades de clojure que conhecemos e amamos, sequibilidade e assim por diante. Deftype deve ser usado como um bloco de construção de baixo nível para a implementação de outras estruturas de dados. Não inclui as interfaces regulares de clojure, mas tem a opção de campos mutáveis ​​(embora este não seja o padrão).

Para ler mais, confira:

A página de tipos de dados clojure.org

O tópico do grupo do Google em que deftype e reify foram introduzidos

Rob Lachlan
fonte
1
O tópico do grupo do google foi muito valioso. Meu entendimento é para proxy java-interop, gen-class são suplantados por reify e deftype principalmente. Fico feliz por termos menos maneiras 'recomendadas' de definir tipos agora.
Salil
2
@ Trylks: a capacidade de tratar o objeto como uma sequência de pares de valores-chave. Quase tudo o que é nativo do clojure pode ser tratado como uma sequência, o que é muito poderoso.
Rob Lachlan
proxyàs vezes é preferível porque permite a modificação de métodos em tempo de execução. (por exemplo, The Joy of Clojure , 2ª ed. sugere que isso pode ser útil para modificar callbacks em um gerenciador da web.)
Marte
52

A resposta curta é que todos eles têm finalidades diferentes e úteis. A complexidade se deve à necessidade de interoperar efetivamente com diferentes recursos da JVM subjacente.

Se você não precisa de nenhuma interoperabilidade Java , em 99% do tempo é melhor usar o defrecord ou um mapa Clojure simples.

  • Use defrecord se quiser usar protocolos
  • Caso contrário, um mapa Clojure regular é provavelmente o mais simples e mais compreensível

Se suas necessidades são mais complexas, o fluxograma a seguir é uma ótima ferramenta para explicar por que você escolheria uma dessas opções em vez das outras:

http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/

Fluxograma para escolher o formulário de definição de tipo de clojure certo

Mikera
fonte
1
muito obrigado pelo link para o fluxograma. Ajuda que o blog seja do co-autor de O'reilly's - Programming Clojure. Acho que essa tomada de decisão é bastante complexa. As diferenças entre interface e classe concreta ou a necessidade de definir métodos estáticos ou não ou tipo nomeado ou tipo anônimo são tão vastas que precisam de uma construção de linguagem diferente?
Salil
4
as diferenças não são vastas, mas são filosoficamente diferentes em termos do que você está tentando alcançar. Acho que as várias abordagens no Clojure refletem essas diferenças subjacentes, o que é um bom motivo pelo qual deveriam receber nomes diferentes.
Mikera
O fluxograma é ótimo, mas não cobre todas as possibilidades ou motivos para usar uma ou outra opção, ou combinações delas. Nenhum gráfico simples poderia.
Marte