2010-10-04 19 views
12

defrecord en clojure permite definir contenedores de datos simples con campos personalizados.¿Macros recomendadas para agregar funcionalidad al constructor defrecord de Clojure?

p. Ej.

user=> (defrecord Book [author title ISBN]) 
user.Book 

El constructor mínima que resulta tarda sólo argumentos posicionales con ninguna funcionalidad adicional, como el impago de los campos, la validación de campos, etc.

user=> (Book. "J.R.R Tolkien" "The Lord of the Rings" 9780618517657) 
#:user.Book{:author "J.R.R Tolkien", :title "The Lord of the Rings", :ISBN 9780618517657} 

Siempre es posible escribir funciones envolviendo el constructor por defecto para obtener Semántica de construcción más compleja: utilizar argumentos de palabra clave, proporcionar valores predeterminados, etc.

Parece el escenario ideal para que una macro proporcione una semántica ampliada. ¿Qué macros han escrito y/o recomendado las personas para la construcción más rica de defrecord?

+0

Debo señalar que defrecord tiene mucho más que ofrecer en términos de cómo funciona con el ecosistema Clojure/Java, los "contenedores de datos simples" anteriores no significan nada rudimentarios, sino todo lo contrario. –

+0

Debe mencionarse que desde clojure 1.3.0, puede hacer '(map-> Book {: author" JRR Tolkien ",: título" The Lord of the Rings ", ISBN 9780618517657)' o '# user.Book {: autor "JRR Tolkien",: título "El señor de los anillos", ISBN 9780618517657} ' – Claude

+0

Para la documentación sobre las características señaladas por Claude, consulte: http://dev.clojure.org/display/design/defrecord+ mejoras –

Respuesta

9

ejemplos de apoyo para las funciones de registro de constructor completas y parciales y soporte para imprimir formularios y pprint eval-poder:

David es un colega mío y estamos utilizando este defrecord2 ampliamente en nuestro proyecto. Creo que algo así debería ser parte del núcleo de Clojure (los detalles pueden variar considerablemente, por supuesto).

Las cosas que hemos encontrado son importantes son:

  • Capacidad de construir un registro con parámetros con nombre (posiblemente parcial): (nueva-foo {: un 1})
  • Capacidad de construir un registro copiando un registro existente y realizando modificaciones: (new-foo old-foo {: a 10})
  • Validación de campo: si pasa un campo fuera de los campos de registro declarados, arroje un error. Por supuesto, esto es legal y potencialmente útil, por lo que hay formas de hacerlo opcional. Como sería raro en nuestro uso, es mucho más probable que sea un error.
  • Valores predeterminados: estos serían muy útiles pero no lo hemos implementado. Chas Emerick ha escrito acerca de agregar soporte para los valores predeterminados aquí: http://cemerick.com/2010/08/02/defrecord-slot-defaults/
  • Soporte de impresión y pprint: nos resulta muy útil tener registros de impresión y impresión en una forma que sea evaluable de nuevo en el registro original. Por ejemplo, esto le permite ejecutar una prueba, deslizar la salida real, verificarla y usarla como salida esperada. O para deslizar la salida desde un seguimiento de depuración y obtener una forma real evaluable.
+0

Definitivamente de acuerdo con un campo no definido que probablemente sea un error (pero útil cuando es intencional). Como es de suponer que uno sabe que el campo de extensión es deliberado, tal vez tenga una función diferente y un error directo. También me pregunto si los metadatos en un registro extendido serían útiles. –

4

Here is one que define un registro con valores predeterminados e invariantes. Crea un código que puede tomar argumentos de palabra clave para establecer los valores de los campos.

(defconstrainedrecord Foo [a 1 b 2] 
    [(every? number? [a b])]) 

(new-Foo) 
;=> #user.Foo{:a 1, :b 2} 

(new-Foo :a 42) 
; #user.Foo{:a 42, :b 2} 

Y como ya he dicho ...invariantes:

(new-Foo :a "bad") 
; AssertionError 

Pero ellos sólo tienen sentido en el contexto de Trammel.

Cuestiones relacionadas