2011-10-26 11 views
7

Parece que hay varias maneras de implementar modelos de datos en Clojure:La implementación de un modelo de datos para evitar errores comunes

  • ordinarios tipos de datos internos (mapas/listas/sistemas/vectores)
  • Built-in tipos de datos + meta-datos - por ejemplo: (type ^{:type ::mytype} {:fieldname 1})
  • tipos de datos incorporados + funciones especiales de descriptor de acceso (por ejemplo, get ting una clave inexistente de un mapa se produce una excepción, en lugar de regresar en silencio nil)
  • DEFTYPE
  • defstruct
  • defrecord
  • defprotocol

Hemos llegado al punto en mapas/listas ya funcionando bien para nosotros no son - nos encontramos con un montón de errores que precondiciones/post- las condiciones podrían atrapar fácilmente, pero tomarse un tiempo muy largo para cazar de otra manera (y es difícil escribir condiciones pre/post efectivas para las funciones que aceptan mapas/listas/vectores anidados) - pero no estamos seguros de cuál de las anteriores Para escoger de.

Tenemos tres objetivos principales:

  • escritura idiomática código Clojure
  • evitar gastar grandes cantidades de tiempo la caza de errores de tipo estúpidos
  • tener confianza en nuestra capacidad para cambiar el código/refactor con silenciosamente rompiendo cualquier cosa

¿Cómo podemos aprovechar el poder de Clojure para h elp nosotros?

Respuesta

4

cultura es Clojure un gran apoyo de los tipos de datos en bruto. Justificadamente, así. Pero los tipos explícitos pueden ser útiles. Cuando sus tipos de datos simples se vuelven suficientemente complejos y específicos, usted esencialmente tiene un tipo de datos implícito sin la especificación.

Confíe en los constructores. Esto suena un poco sucio, en una especie de OOP, pero un constructor no es más que una función que crea su tipo de datos de manera segura y conveniente. Un inconveniente de las estructuras de datos simples es que fomentan la creación de datos sobre la marcha. Entonces, en lugar de llamar (myconstructor ...), intento componer mis datos directamente. Y con mucho potencial de error, así como también problemas si necesito cambiar el tipo de datos subyacente.

Los registros son el punto dulce. Con todo el alboroto sobre los tipos de datos en bruto, es fácil olvidar que los registros hacen muchas cosas que los mapas pueden hacer. Se puede acceder de la misma manera. Puedes llamar a seq sobre ellos. Puedes desestructurarlos de la misma manera. La gran mayoría de las funciones que esperan un mapa también aceptarán un registro.

los metadatos no se salvarán. Mi principal objeción a confiar en metadatos es que no se refleja en igualdad.

user> (= (with-meta [1 2 3] {:type :A}) (with-meta [1 2 3] {:type :B})) 
true 

Si eso es aceptable o no depende de usted, pero me preocuparía la introducción de nuevos errores sutiles.


Las otras opciones dataype:

  • DEFTYPE es sólo para el trabajo bajo nivel en la creación de nuevas estructuras de datos con fines básicos o especiales. A diferencia del defrecord, no trae todo el bien de Clojure junto con él. Para la mayoría del trabajo, no es necesario ni aconsejable.
  • defstruct debe estar en desuso. Cuando Rich Hickey introdujo tipos y protocolos, dijo esencialmente que el defrecord debería preferirse cada vez más.

Los protocolos son muy útiles, a pesar de que se siente como un poco de una desviación de la (+ datos de funciones) paradigma. Si se encuentra creando registros, también debería considerar la definición de protocolos.

EDIT: Descubrí otra ventaja para los tipos de datos simples que no me resultaban evidentes anteriormente: si está haciendo programación web, los tipos de datos simples se pueden convertir ay desde JSON de manera fácil y eficiente. (Las bibliotecas para hacer esto incluyen clojure.data.json, clj-json, y mi favorita, cheshire). Con registros y tipos de datos, la tarea es considerablemente más molesta.

+0

Está bien, entonces necesito averiguar 'defrecord' y' defprotocol', puedo ignorar 'defstruct', y no tengo que preocuparme demasiado por' deftype'. ¿Importa a un programa clojure, que 'defrecord' crea código java, en el sentido de que no quiero preocuparme por tener una clase java, pero si clojure quiere usar uno en privado, está bien? Gran respuesta, muy atento. –

+0

Bueno, un mapa simple es una clase Java también, como se puede ver en https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java –

+0

Así que no es realmente una gran oferta que defrecord crea una clase java. Desde nuestro punto de vista usando el registro, no hará mucha diferencia, a excepción de la construcción, se sentirá como datos de clojure. –

1

Es realmente conveniente poder componer funciones que funcionan en mapas y listas, y sería una pena perder eso al cambiar a clases y protocolos. después de todo, es mejor tener cien funciones en un tipo. Cambiar a protocolos o registros sería un poco pesado. Le impediría (debug (map :rows (get-state)) al depurar, por ejemplo.

metadatos es una gran manera de agregar "el tipo suficiente" para hacer que sus datos sean más seguros en los lugares que lo necesitan sin perder los beneficios en el resto de su base de código. Lo recomendaría ir con la opción 2

  • 'tipos de datos incorporados + meta-datos ((escriba^{: Tipo :: MyType} {: nombre de campo 1}))'
+0

¿No es Alan Perlis-ism unas cien funciones en un tipo de datos? Volví a formatear ese ejemplo en el título, accidentalmente lo puse en paréntesis que no sean LISP. –

+0

sí, esa es la cita que estaba buscando, crédito donde se debe crédito! –

Cuestiones relacionadas