2012-02-11 9 views
6

Provenendo da uno sfondo Java, mi piace molto la sicurezza di tipo statico e mi chiedo come i programmatori di clojure affrontano il problema delle definizioni di formato dei dati (forse non solo tipi ma invarianti generali, perché i tipi sono solo un caso speciale di quello.)Formato dati sicurezza in clojure

Questo è simile a una domanda esistente "Digitare sicurezza in Clojure", ma che si concentra più sull'aspetto di come controllare i tipi in fase di compilazione, mentre sono più interessato a come il problema è affrontato pragmaticamente.

Come esempio pratico sto considerando un'applicazione editor che gestisce un particolare formato di documento. Ogni documento è composto da elementi che si presentano in diverse varietà (elementi grafici, elementi font ecc.) Ci sarebbero editor per i diversi tipi di elementi, e naturalmente anche funzioni per trasformare un documento da/a un flusso di byte nella sua lingua nativa. formato del disco.

Il problema di base che mi interessa è che gli editor e le funzioni di lettura/scrittura devono concordare un formato dati comune. In Java, modellerei i dati del documento come un oggetto grafico, ad es. con una classe che rappresenta un documento e una classe per ogni varietà di elementi. In questo modo, ottengo una garanzia in fase di compilazione su come appare la struttura dei miei dati, e che il campo "width" di un elemento grafico è un numero intero e non un float. Non garantisce che la larghezza sia positiva, ma l'utilizzo di un'interfaccia getter/setter consentirebbe alla classe corrispondente di aggiungere garanzie invarianti del genere.

Essere in grado di fare affidamento su questo rende più semplice il codice che si occupa di questi dati e le violazioni del formato possono essere catturate in fase di compilazione o all'inizio del runtime (dove alcuni tentativi di codice modificano i dati che violano gli invarianti).

Come si può ottenere una simile "affidabilità del formato dati" in Clojure? Per quanto ne so, non c'è modo di eseguire il controllo in fase di compilazione e nascondere i dati di dominio dietro un'interfaccia di funzione sembra scoraggiarsi come non idiomatici (o forse mi fraintendono?), Quindi cosa fanno gli sviluppatori Clojure per sentirsi sicuri riguardo il formato dei dati consegnati nelle loro funzioni? Come si ottiene il codice in errore il più rapidamente possibile, e non dopo che l'utente ha modificato per altri 20 minuti e tenta di salvare su disco, quando la funzione di salvataggio rileva che c'è un elemento grafico nell'elenco dei caratteri a causa di un bug dell'editor?

Si prega di notare che io sono interessato a Clojure e di apprendimento, ma non ho scritto alcun software vero e proprio con esso ancora, quindi è possibile che io sono solo confuso e la risposta è molto semplice - se è così, mi dispiace per sprecare il tuo tempo :).

risposta

8

Non vedo nulla di sbagliato o unidiomatico sull'utilizzo di un'API di convalida per costruire e manipolare i dati come di seguito.

(defn text-box [text height width] 
    {:pre [(string? text) (integer? height) (integer? width)]} 
    {:type 'text-box :text text :height height :width width}) 

(defn colorize [thing color] 
    {:pre [(valid-color? color)]} 
    (assoc thing :color color)) 

... (colorize (text-box "Hi!" 20 30) :green) ... 

Inoltre, i riferimenti (vars, arbitri, atomi, agenti) può avere associato un validator function che può essere utilizzato per garantire uno stato valido in ogni momento.

+0

Grazie per le risposte finora. Poiché questa è una domanda piuttosto aperta, aspetterò un po 'di più prima di accettare comunque. La funzione di validazione suona come una soluzione interessante, sebbene (rimanendo con l'esempio) se si dispone di un documento di grandi dimensioni come stato, convalidarlo completamente ogni volta che viene apportata una piccola modifica sembra essere inefficiente - in tale modello, suppongo che usando la convalida le funzioni di manipolazione sarebbero preferibili. – Medo42

5

Buona domanda: trovo anche che passare da un linguaggio tipizzato staticamente a uno dinamico richiede un po 'più di attenzione sulla sicurezza del tipo. Fortunatamente le tecniche TDD aiutano moltissimo qui.

Scrivo in genere una funzione di "convalida" che verifica tutte le ipotesi sulla struttura dati. Lo faccio spesso anche in Java per ipotesi invarianti, ma in Clojure è più importante perché devi controllare anche i tipi di pensiero.

È quindi possibile utilizzare la funzione validate in diversi modi:

  • Come un rapido controllo al REPL: (validate foo)
  • In unit test: (is (validate (new-foo-from-template a b c)))
  • Come controllo run-time per le funzioni chiave , per esempio controllando che (read-foo some-foo-input-stream) vale

Se si dispone di una struttura di dati complessa che è un albero di molteplici tipi di componenti differenti, è possibile scrivere una funzione di convalida per ogni tipo di componente e hanno la funzione di convalida per l'intero bando del documento di convalida per ciascun sottocomponente in modo ricorsivo. Un bel trucco è usare i protocolli oi multimetodi per rendere la funzione di validazione polimorfica per ogni tipo di componente.

Problemi correlati