2010-10-04 8 views
12

defrecord in clojure consente di definire semplici contenitori di dati con campi personalizzati.Macro consigliate per aggiungere funzionalità al costruttore di defrecord di Clojure?

ad es.

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

Il costruttore minima che si traduce in soli argomenti posizionali senza funzionalità aggiuntive come ad esempio inadempiente dei campi, la convalida di campo ecc

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} 

E 'sempre possibile scrivere funzioni avvolgendo il costruttore di default per ottenere semantica della costruzione più complessa: utilizzo di argomenti di parole chiave, fornitura di valori predefiniti e così via.

Questo sembra lo scenario ideale per una macro per fornire semantica espansa. Quali macro hanno scritto e/o raccomandato persone per la costruzione più ricca defrecord?

+0

Vorrei sottolineare che defrecord ha molto più successo in termini di come funziona con l'ecosistema Clojure/Java, "contenitori di dati semplici" sopra non significa affatto rudimentale, anzi il contrario. –

+0

Va detto che da clojure 1.3.0, puoi fare '(mappa-> Libro {: autore" JRR Tolkien ",: titolo" Il Signore degli Anelli ",: ISBN 9780618517657)' o '# user.Book {: autore "JRR Tolkien",: titolo "Il signore degli anelli",: ISBN 9780618517657} ' – Claude

+0

Per la documentazione sulle caratteristiche annotate da Claude, vedere: http://dev.clojure.org/display/design/defrecord+ miglioramenti –

risposta

9

Esempi di sostegno per le funzioni di registrazione costruttore totali e parziali e il supporto per le forme di stampa e pprint eval-grado:

David è un mio collega e stiamo usando questa defrecord2 ampiamente il nostro progetto. Penso che qualcosa del genere dovrebbe essere parte del core di Clojure (i dettagli potrebbero variare notevolmente, ovviamente).

Le cose che abbiamo trovato ad essere importanti sono:

  • capacità di costruire un record con nome (possibilmente parziali) parametri: (new-foo {: un 1})
  • capacità di costruire un record copiando un record esistente e apportando modifiche: (new-foo old-foo {: a 10})
  • Convalida del campo: se si passa un campo al di fuori dei campi del record dichiarato, si genera un errore. Naturalmente, questo è effettivamente legale e potenzialmente utile, quindi ci sono modi per renderlo opzionale. Dal momento che sarebbe raro nel nostro utilizzo, è molto più probabile che si tratti di un errore.
  • Valori predefiniti - questi sarebbero molto utili ma non l'abbiamo implementato. Chas Emerick ha scritto sull'aggiunta del supporto per i valori predefiniti qui: http://cemerick.com/2010/08/02/defrecord-slot-defaults/
  • Supporto stampa e pprint - troviamo molto utile avere i record di stampa e pprint in un formato che è in grado di tornare al record originale. Ad esempio, questo consente di eseguire un test, scorrere l'output effettivo, verificarlo e usarlo come output previsto. O per scorrere l'output da una traccia di debug e ottenere una forma eval-real reale.
+0

Definitivamente d'accordo con un campo non definito che potrebbe essere un errore (ma utile quando intenzionale). Come presumibilmente si sa che il campo di estensione è intenzionale, dovrebbe forse avere una funzione diversa e l'associazione diritta genera un errore. Mi chiedo anche se i meta-dati su una registrazione estesa sarebbero utili. –

4

Here is one che definisce un record con valori predefiniti e invarianti. Crea un ctor che può utilizzare parole chiave arg per impostare i valori dei campi.

(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} 

E come ho detto ...invarianti:

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

Ma loro senso solo nel contesto di Trammel.