2013-08-16 12 views
5

Próbuję dokonać serializacji/deserializacji za pomocą odczytu i pokazać (co nie jest problemem per se), ale rozszerzalne w tym sensie, że dane typu można rozszerzyć (ale nie skurczyć) .Rozszerzalna serializacja w Haskell

Załóżmy, że mam tego typu:

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

a lista:

foos = [Foo 1, Foo 2] 

mogę łatwo deserializować go do pliku:

hPutStrLn fileHand . ppShow $ foos 

Wtedy możemy szeregować je z powrotem :

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

Ale przypuśćmy, że kilka miesięcy później chcę dodać pole "baz" do typu Foo . Bezpośrednia serializacja z pliku w starym formacie nie będzie już działać z czytaniem, będę musiał przekonwertować plik (czego tak naprawdę nie chcę ).

Czy istnieje elegancki (bez wprowadzania jawnej logiki wersjonowania w samym programie) rozwiązanie, aby nadal serializować dane z pliku i wypełniać brakujące pola wartościami domyślnymi? Może niektóre sztuczki?

Dzięki.

Odpowiedz

2

Tak. Wystarczy dodać polimorficzny murawę:

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

następnie zdefiniować instancję serializacji z ograniczeniem, że a musi być możliwy do serializacji:

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

Kiedy nie używasz dodatkowego pola, po prostu włożyć do niego () , ponieważ () jest trywialnie szeregowalny (i ma już instancję Serialize).

Edytuj: Ups, właśnie uświadomiłeś sobie, że mówisz o ładnym druku. Równowartość rozwiązaniem jest zdefiniowanie klasy typu takiego:

class PrettyPrint a where 
    pp :: a -> String 

instance PrettyPrint() where 
    pp() = "" 

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

To może nie być to, czego szukasz, ponieważ chcemy uniknąć wyraźnego wersjonowanie ale bym jeszcze przypomnieć, safecopy która jest go- do rozwiązania dla serializacji szeregowej i przynajmniej sprawia, że ​​jest trochę bezbolesna.

Nie sądzę, istnieje jakikolwiek sposób, aby użyć domyślnego Show i Read instancji przy jednoczesnym wspieraniu dodawanie dowolnej ilości nowych dziedzinach, ale można oczywiście napisać własny Read instancji przez strony, które obsługuje brakujące pola rekordu. Uważam jednak, że jest to bardziej pracochłonne i podatne na błędy niż używanie tylko safecopy.

+0

Nigdy nie słyszałem o SafeCopy. Nifty :-) – luqui

+0

+1 do zabezpieczenia. Na pewno go użyję. – insitu

3

Chcąc zmienić układ danych, wciąż będąc w stanie uzyskać dostęp do twoich rzeczy, jest jednym z z definiującym motywacje do wymyślania systemów zarządzania bazami danych. Czy zastanawiałeś się, czy po prostu upuszczasz swoje dane do prostej tabeli SQLite?To może być przesada w stosunku do tego, co próbujesz zrobić, ale ma kilka zalet:

  • Prawie na pewno wydajniejsza niż kodowanie tekstowe.
  • Nadal można je łatwo odczytać spoza aplikacji (np. W celu sprawdzenia, czy zapisano poprawną treść).
  • "Konwersja pliku" oznacza teraz proste zapytanie SQL.
  • Jeśli nie używasz * jako selektora kolumn, Twój stary kod nadal może odczytać rzeczy utworzone przez nowsze wersje aplikacji. (Tj., do przodu kompatybilność jak również wstecz).
  • Alternatywnie, możesz przeczytać napisać prosty kod, który odczytuje schemat DB i dostarcza wartości domyślne dla kolumn, które jeszcze nie istnieją.

Nie wiem, czy to jest odpowiednie dla twojej sprawy, ale warto o tym pomyśleć.

4

W zależności od przypadku użycia można również użyć persistent z Yesod, aby utrwalić dane w bazie danych. Cytowanie:

Trwałe jest zgodne z zasadami przewodnimi bezpieczeństwa typu i zwięzłą, deklaratywną składnią. Niektóre inne fajne funkcje to:

  • Nieobsługiwana przez bazę danych. Obsługa PostgreSQL, SQLite, MySQL i MongoDB jest w pierwszej klasie z eksperymentalną obsługą CouchDB w pracach.
  • Dzięki temu, że nie jesteśmy związani z relacjami, możemy jednocześnie obsługiwać większą liczbę warstw pamięci i nie są ograniczane przez niektóre wąskie gardła występujące podczas łączenia.
  • Głównym źródłem frustracji podczas pracy z bazami danych SQL są zmiany w schemacie. Trwałe może automatycznie wykonywać migracje bazy danych.

Persistent obsługuje zmian w danych dla was w tych przypadkach:

dla następujących przypadkach będzie automatycznie zmieniać schemat:

  • Typ danych zmieniono pole. Baza danych może jednak wyrazić sprzeciw wobec tej modyfikacji, jeśli danych nie można przetłumaczyć.
  • Dodano pole. Jeśli jednak pole nie ma wartości NULL, nie podano wartości domyślnej (omówimy domyślne wartości później) i dane już są w bazie danych, baza danych nie pozwoli na to.
  • Pole jest przekształcane z wartości niezerowej na pustą. W przeciwnym przypadku Persistent podejmie próbę konwersji, zależnie od zgody bazy danych.
  • Dodano nowy podmiot.