¿Por qué Clojure tiene 5 formas de definir una clase en lugar de solo una?

84

Clojure tiene gen-class, reify, proxy y también deftype y defrecord para definir nuevos tipos de datos de clase. Para un lenguaje que valora la simplicidad sintáctica y aborrece la complejidad innecesaria, parece una aberración. ¿Alguien podría explicar por qué es así? ¿Podría haber sido suficiente la defclass estilo Common Lisp?

Salil
fuente

Respuestas:

90

Esta es una combinación de tres factores diferentes:

  1. El sistema de tipos particular de la jvm
  2. La necesidad de una semántica ligeramente diferente para diferentes casos de uso al definir tipos
  3. El hecho de que algunos de estos se desarrollaron antes y otros más tarde, a medida que el lenguaje ha evolucionado.

Primero, consideremos qué hacen estos. deftype y gen-class son similares en el sentido de que ambos definen una clase con nombre para la compilación anticipada. Gen-class vino primero, seguido de deftype en clojure 1.2. Se prefiere Deftype y tiene mejores características de rendimiento, pero es más restrictivo. Una clase deftype puede ajustarse a una interfaz, pero no puede heredar de otra clase.

Reify y proxy se utilizan para crear dinámicamente una instancia de una clase anónima en tiempo de ejecución. Proxy vino primero, reify vino junto con deftype y defrecord en clojure 1.2. Se prefiere Reify, al igual que deftype, donde la semántica no es demasiado restrictiva.

Eso deja la pregunta de por qué deftype y defrecord, ya que aparecieron al mismo tiempo y tienen un papel similar. Para la mayoría de los propósitos, querremos usar defrecord: tiene todas las bondades de clojure que conocemos y amamos, sequability, etc. Deftype está diseñado para usarse como un bloque de construcción de bajo nivel para la implementación de otras estructuras de datos. No incluye las interfaces regulares de clojure, pero tiene la opción de campos mutables (aunque este no es el predeterminado).

Para obtener más información, consulte:

La página de tipos de datos de clojure.org

El hilo del grupo de Google donde se introdujeron deftype y reify

Rob Lachlan
fuente
1
El hilo del grupo de Google fue muy valioso. Tengo entendido que para el proxy de interoperabilidad java, gen-class son reemplazados por reify y deftype principalmente. Me alegra que ahora tengamos menos formas "recomendadas" para definir tipos.
Salil
2
@ Trylks: la capacidad de tratar el objeto como una secuencia de pares clave-valor. Prácticamente todo lo que es nativo de clojure se puede tratar como una secuencia, que es muy poderosa.
Rob Lachlan
proxya veces se prefiere porque permite la modificación de métodos en tiempo de ejecución. (por ejemplo, The Joy of Clojure , 2ª ed. sugiere que esto podría ser útil para modificar devoluciones de llamada en un controlador web).
Mars
52

La respuesta corta es que todos tienen propósitos diferentes y útiles. La complejidad se debe a la necesidad de interoperar de manera efectiva con diferentes características de la JVM subyacente.

Si no necesita ninguna interoperabilidad de Java , el 99% de las veces es mejor que se quede con defrecord o un simple mapa de Clojure.

  • Utilice defrecord si desea utilizar protocolos
  • De lo contrario, un mapa de Clojure normal es probablemente el más simple y comprensible

Si sus necesidades son más complejas, el siguiente diagrama de flujo es una gran herramienta para explicar por qué elegiría una de estas opciones sobre las otras:

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

Diagrama de flujo para elegir el formulario de definición de tipo de clojure correcto

mikera
fuente
1
muchas gracias por el enlace al diagrama de flujo. Ayuda que el blog sea del coautor de O'reilly's - Programming Clojure. Creo que esta toma de decisiones es bastante compleja. ¿Son las diferencias entre la interfaz y la clase concreta o es necesario definir métodos estáticos o no o el tipo con nombre o el tipo anónimo son tan grandes que necesitan una construcción de lenguaje diferente?
Salil
4
las diferencias no son enormes, pero son filosóficamente diferentes en términos de lo que está tratando de lograr. Creo que los múltiples enfoques en Clojure reflejan estas diferencias subyacentes, lo cual es una buena razón por la que deberían recibir nombres diferentes.
mikera
El diagrama de flujo es excelente, pero no cubre todas las posibilidades o razones para usar una u otra opción, o combinaciones de ellas. Ningún gráfico simple podría hacerlo.
Marte