2013-08-16 5 views
5

Sto tentando di effettuare una serializzazione/deserializzazione utilizzando read e show (che non è un problema di per sé), ma estensibile nel senso che il tipo di dati può essere esteso (ma non ridotto) .Serializzazione estendibile in Haskell

Supponiamo che io sono questo tipo:

data Foo = { bar :: Int } deriving (Show, Read) 

E l'elenco:

foos = [Foo 1, Foo 2] 

mi può facilmente deserializzare in un file:

hPutStrLn fileHand . ppShow $ foos 

allora posso serializzare indietro :

!str <- hGetContents fileHand 
let foosFromFile = fromMaybe [] $ (readMaybe :: String -> Maybe [Foo]) str 

Ma supponiamo che mesi dopo voglio aggiungere un campo "baz" nel tipo di Foo . La serializzazione diretta dal file di vecchio formato non funzionerà più con con lettura, sarà necessario convertire il file (che in realtà non lo desidero ).

Quindi, esiste una soluzione elegante (senza inserire una logica esplicita di versioni nel programma stesso) per serializzare ancora i dati da un file e riempire i campi mancanti con i valori predefiniti? Forse alcuni tipi di trucchi?

Grazie.

risposta

2

Sì. Basta aggiungere un campo polimorfico:

data Foo a = { bar :: Int, extra :: a } deriving (Show, Read) 

quindi definire un'istanza di serializzazione con il vincolo che a deve essere serializzabile:

instance (Serialize a) => Serialize (Foo a) where ... 

Quando non stai utilizzando il campo in più, basta inserire una () in esso , dal momento che () è banalmente serializzabile (e ha già un'istanza Serialize).

Modifica: Oops, ho appena realizzato che stai parlando di una bella stampa. La soluzione equivalente è quella di definire una classe tipo come questo:

class PrettyPrint a where 
    pp :: a -> String 

instance PrettyPrint() where 
    pp() = "" 

instance (PrettyPrint a) => PrettyPrint (Foo a) where 
    pp = ... -- You fill this in 
9

questo potrebbe non essere quello che stai cercando per quanto si vuole evitare il controllo delle versioni esplicito, ma mi piace ancora sottolineare safecopy che è il go- alla soluzione per la serializzazione con versione e almeno lo rende un po 'indolore.

Non credo che ci sia alcun modo per utilizzare l'impostazione predefinita Show e Read casi pur sostenendo l'aggiunta di una quantità arbitraria di nuovi campi, ma si può ovviamente scrivere il proprio Read esempio a mano che gestisce i campi dei record mancanti. Tuttavia, penso che sia più laborioso e incline all'errore rispetto all'utilizzo di safecopy.

+0

Non avevo mai sentito parlare di SafeCopy. Nifty :-) – luqui

+0

+1 per safecopy. Lo userò sicuramente. – insitu

3

Se si desidera modificare il layout dei dati mentre si è ancora in grado di accedere ai propri file, è più o meno lo il che definisce le motivazioni per l'invenzione dei sistemi di gestione dei database. Hai considerato di lasciar cadere i tuoi dati in una semplice tabella SQLite?Potrebbe essere eccessivo per quello che stai cercando di fare, ma ha alcuni vantaggi:

  • Quasi certamente più efficiente di una codifica basata su testo.
  • È ancora possibile leggerlo facilmente dall'esterno dell'applicazione (ad esempio, per verificare che la cosa corretta sia stata salvata).
  • "Conversione del file" ora equivale a una semplice query SQL.
  • Se si evita di utilizzare * come selettore di colonna, il vecchio codice può ancora leggere elementi creati dalle versioni più recenti dell'applicazione. (Ad esempio, inoltra la compatibilità e viceversa).
  • In alternativa, è possibile leggere scrivere un semplice codice boilerplate che legge lo schema DB e fornisce valori predefiniti per tutte le colonne che non esistono ancora.

Non so se questo è appropriato per il tuo caso, ma vale la pena pensarci.

4

A seconda del caso d'uso, è possibile utilizzare persistent da Yesod per mantenere i dati in un database. Citando:

Persistente segue i principi guida della sicurezza del tipo e sintassi concisa e dichiarativa. Alcune altre caratteristiche interessanti sono:

  • Database-agnostico. C'è un supporto di prima classe per PostgreSQL, SQLite, MySQL e MongoDB, con il supporto sperimentale CouchDB nelle opere.
  • Essendo di natura non relazionale, siamo contemporaneamente in grado di supportare un numero più ampio di livelli di storage e non sono vincolati da alcuni dei colli di bottiglia delle prestazioni sostenuti attraverso i join.
  • Una delle principali cause di frustrazione nell'affrontare i database SQL sono le modifiche allo schema. Persistent può eseguire automaticamente migrazioni di database.

persistente gestisce le modifiche nei dati per voi in questi casi:

Per i seguenti casi, si altera automaticamente lo schema:

  • il tipo di dati un campo è cambiato. Tuttavia, il database può opporsi a questa modifica se i dati non possono essere tradotti.
  • È stato aggiunto un campo. Tuttavia, se il campo non è nullo, non viene fornito alcun valore predefinito (discuteremo i valori predefiniti in seguito) e sono già presenti dati nel database, il database non consentirà che ciò accada.
  • Un campo viene convertito da non null a null. Nel caso opposto, Persistent tenterà la conversione, in base all'approvazione del database.
  • È stata aggiunta una nuova entità.