Попробовал написать сериализацию с помощью Data.Binary и наткнулся на бяку при рекурсивных данных.
data A = A {a_unuqId :: Int, a_refs :: [B]}
data B = B {b_unuqId :: Int, b_refs :: [A]}
saveABtoFile :: FilePath -> ([A], [B]) -> IO ()
saveABtoFile = Data.Binary.encodeFile
loadABfromFile :: FilePath -> IO (([A], [B]))
loadABfromFile = Data.Binary.decodeFile
С сохранением всё ясно, вместо сохранения списка ссылок сохраняем список id-ов:
instance Binary A where
put (A i refs) = put i >> put (map b_unuqId refs)
А вот как написать get?
Можно конечно написать как-то так:
instance Binary A where
get = do
i <- get
b_ids <- get
return $ A i (map (\bi -> B bi []) b_ids
loadABfromFile = do
(xa, xb) <- Data.Binary.decodeFile
let xa' = map createA xa
xb' = map createB xb
createA (A i refs) = A i $ map (\b -> fromJast . find (== (b_unuqId b)) $ xb') refs
createB (B i refs) = B i $ map (\a -> fromJast . find (== (a_unuqId a)) $ xa') refs
return (xa', xb')
Но как-то это некрасиво...
Есть ещё какие-нибудь способы?
Или лучше сразу хранить id-ешки и при необходимости шерстить соответствующий список/мап/хешь?
П. С. Нашел ссылки на несколько библиотек сериализации. Некоторые умеют вроде сами такое разруливать. А Булатовская по описанию страшно быстрая и автоматом строит инстансы.
Но кроме Data.Binary нет ни одной живой.
Раскопки натолкнули на PicklerCombinators но найти живых библиотек не удалось.
Кроме того, нашел в составе Leksah-а модуль Data.Binary.Shared который умеет работать с совмещением данных.
Он оборачивает работу с монадами Put и Get в монаду состояния — так что на его примере можно написать свои трансформации.
Но как-то всё это грустно — нет готовых простых решений для задачи сериализации состояний. Data.Binary — это всё же низкоуровневый разбор бинарных форматов.
Аналог в python-е — это модуль struct.
А вот аналогов pickle или хотя бы marshal пока не наблюдается...
Здравствуйте, Tonal-, Вы писали:
T>Попробовал написать сериализацию с помощью Data.Binary и наткнулся на бяку при рекурсивных данных.
T>data A = A {a_unuqId :: Int, a_refs :: [B]}
T>data B = B {b_unuqId :: Int, b_refs :: [A]}
Позвольте, высокомудрые сэры, мне, скудному умом и бедному практикой хаскелла, влезть в вашу преинтересную беседу.
Сериализация рекурсивных данных может быть сделана 2 способами.
1) обход вглубь: запись вариантных структур: либо литерал со всем его потрохами, либо идентификатор ранее записанного литерала
2) обход вширь: запись литералов, заменяя потроха на идентификаторы грядущих литералов (в очереди на запись)
Десериализация, соответственно, выглядит так
1) если встретили литерал — добавляем в словарь и углубляемся в чтение; если метку — читаем из словаря
2) читаем промежуточные структуры (литералы с метками потрохов), собирая весь словарь; затем углубляемся, заменяя метки на реальные значения
Здравствуйте, Кодт, Вы писали:
К>Позвольте, высокомудрые сэры, мне, скудному умом и бедному практикой хаскелла, влезть в вашу преинтересную беседу. К>Сериализация рекурсивных данных может быть сделана 2 способами.
Это всё понятно. Руками Tonal- сначала и сделал. Но при наличие таких мощных библиотек как, например, uniplate — так делать обидно.