Сериализация взаимно-рекурсивных данных
Feb. 5th, 2010 01:23 pmНа RSDN Tonal- задаёт вопрос о сериализации взаимно-рекурсивных данных.
Очевидно, что при сериализации рекурсивных данных необходимо не сериализовать уже сериализованные данные, дабы избежать цикла. Для этого в том месте, где имеется связь со следующим рекурсивным звеном, можно сохранить ссылку.
Поэтому в голову приходит замечательный пакет data-reify от Andy Gill. О его применении можно прочитать в статье Type-Safe Observable Sharing in Haskell.
Смысл в том, что мы можем разложить рекурсивные данные на нерекурсивные данные со ссылками - то, что нам и нужно.
Т.е. для типов из примера Tonal-
это будет
Обратите внимание на дырки
Пишем instance для MuRef
и получаем нужный граф, например
или
Меня здесь смущает тип
При десериализации придётся обращать граф в конкретный тип, например,
Покритикуйте.
Очевидно, что при сериализации рекурсивных данных необходимо не сериализовать уже сериализованные данные, дабы избежать цикла. Для этого в том месте, где имеется связь со следующим рекурсивным звеном, можно сохранить ссылку.
Поэтому в голову приходит замечательный пакет data-reify от Andy Gill. О его применении можно прочитать в статье Type-Safe Observable Sharing in Haskell.
Смысл в том, что мы можем разложить рекурсивные данные на нерекурсивные данные со ссылками - то, что нам и нужно.
Т.е. для типов из примера Tonal-
data A = A {a_unuqId :: Int, a_refs :: [B]}
data B = B {b_unuqId :: Int, b_refs :: [A]}
это будет
data N u = AN { an_uniqId :: Int, an_refs :: [u] }
| BN { bn_uniqId :: Int, bn_refs :: [u] }
Обратите внимание на дырки
`u', оставленные для рекурсии - по сути A ~ N B и наоборот.Пишем instance для MuRef
instance MuRef A where
type DeRef A = N
mapDeRef f (A i rs) = AN i <$> sequenceA (map f rs)
instance MuRef B where
type DeRef B = N
mapDeRef f (B i rs) = BN i <$> sequenceA (map f rs)
и получаем нужный граф, например
> let { a = A 1 [b] ; b = B 2 [a] }
> reifyGraph a
let [(1,AN {an_uniqId = 1, an_refs = [2]}),(2,BN {bn_uniqId = 2, bn_refs = [1]})] in 1
или
> let { a1 = A 1 [b1, b2] ; a2 = A 2 [b2] ; b1 = B 3 [a1] ; b2 = B 4 [a1, a2] }
> reifyGraph a1
let [(1,AN {an_uniqId = 1, an_refs = [2,3]}),
(3,BN {bn_uniqId = 4, bn_refs = [1,4]}),
(4,AN {an_uniqId = 2, an_refs = [3]}),
(2,BN {bn_uniqId = 3, bn_refs = [1]})] in 1
Меня здесь смущает тип
[(Unique, g Unique)], всё таки хочется IntMap (g Unique) - быстрее и десериализация будет проще. Впрочем, переписать библиотеку должно быть несложно.При десериализации придётся обращать граф в конкретный тип, например,
A. Это дополнительные телодвижения, но зато весь код у нас более общий и не привязан конкретно к сериализации. Например, необходимо использовать мемоизацию:
graphToA :: Graph (DeRef A) -> A
graphToA (Graph gs i) = ma ! i
where
ma = fromList [ (i, A ai $ map (mb!) rs) | (i, AN ai rs) <- gs ]
mb = fromList [ (i, B bi $ map (ma!) rs) | (i, BN bi rs) <- gs ]
Покритикуйте.
no subject
Date: 2010-02-05 01:55 pm (UTC)Надо будет подумать, как это получше написать.
no subject
Date: 2010-02-05 02:01 pm (UTC)no subject
Date: 2010-02-05 02:30 pm (UTC)data N :: * -> * -> * where N_A :: Int -> [u] -> N A u N_B :: Int -> [u] -> N B uno subject
Date: 2010-02-05 01:56 pm (UTC)no subject
Date: 2010-02-05 02:02 pm (UTC)[ id | A id _ <- universeBi a ]
чтобы получить _конечный_ список всех нужных полей.
спасибо
Date: 2010-02-05 02:41 pm (UTC)no subject
Date: 2010-02-05 06:22 pm (UTC)no subject
Date: 2010-02-06 07:07 am (UTC)no subject
Date: 2010-02-06 11:09 am (UTC)для
data A = A {a_unuqId :: Int, a_refs :: [B]}
data B = B {b_unuqId :: Int, b_refs :: [A]}
сериализуем в монаде
type Ser a = StateT (Set.Set Int) (Writer [Word8]) a
посещённые Id добавляем в стейт, рекурсивный обход можно делать чем угодно, главное, чтоб было удобно.
Чем плохо такое решение в лоб ?
no subject
Date: 2010-02-06 09:44 pm (UTC)no subject
Date: 2010-02-07 07:36 am (UTC)no subject
Date: 2010-02-07 07:37 am (UTC)no subject
Date: 2010-02-07 07:57 am (UTC)no subject
Date: 2010-02-07 08:50 am (UTC)no subject
Date: 2010-02-07 11:40 am (UTC)module Ser where import Data.Generics import Control.Monad.State import Control.Monad.Writer import Control.Monad.Identity import qualified Data.Set as Set import Data.Maybe ifIsId :: (Monad m, MonadState (Set.Set Id) m ) => Id -> m () -> m () -> m () ifIsId iD onS onF = do s <- get let is = Set.member iD s if is then onS else onF newtype Id = Id { unId :: Int} deriving (Data,Typeable,Show,Read,Ord,Eq) getIdMaybe :: (Data a, Typeable a) => a -> Maybe Id -- ^ get Id for this node. -- Limitation : no more then one id per node or undefined behaivor getIdMaybe = listToMaybe . catMaybes . (gmapQ (mkQ Nothing (Just :: Id -> Maybe Id))) type SerM a = StateT (Set.Set Id) (WriterT String Identity) a serV :: (Data v, Typeable v) => v -> SerM () serV v = do tell "{ " case (getIdMaybe v) of -- brunch if value has identifier Just iD -> ifIsId iD ( tell $ "linkTo " ++ (show $ unId iD) ) (serRi v iD) Nothing -> (serRs v) tell " }" where serRi v iD = (modify $ Set.insert iD) >> tell (showConstr $ toConstr v) >> tell (" id = " ++ (show $ unId iD) ) >> (sequence_ $ gmapQ serV v) serRs v = tell (showConstr $ toConstr v) >> (sequence_ $ gmapQ serV v) runSer :: (Data v, Typeable v) => v -> String runSer v = runIdentity $ execWriterT $ evalStateT (serV v) Set.empty data A = A { id_A :: Id , ref_B :: Maybe B } deriving (Data,Typeable) data B = B { id_B :: Id , ref_A :: Maybe A, ref_C :: Maybe C } deriving (Data,Typeable) data C = C { ind_ref_A :: Maybe A } deriving (Data,Typeable) test :: A test = let top = A (Id 1) (Just sb) sb = B (Id 2) (Just top) (Just sc) sc = C (Just top) in topТест:
*Ser> runSer test "{ A id = 1{ Id{ 1 } }{ Just{ B id = 2{ Id{ 2 } }{ Just{ linkTo 1 } }{ Just{ C{ Just{ linkTo 1 } } } } } } }"Десериализацию с ходу не напишу, надо будет gread покурить. По идее, пишется.
no subject
Date: 2010-02-08 06:58 am (UTC)Версия, которая не зависит от Id (пользую StableName)
http://hpaste.org/fastcgi/hpaste.fcgi/view?id=17329#a17329
В uniplate, чтобы поддерживал рекурсивные данные, это красиво не засунешь :-( Потому что IO.
no subject
Date: 2010-02-08 07:06 am (UTC)no subject
Date: 2010-02-08 07:08 am (UTC){-# LANGUAGE DeriveDataTypeable, FlexibleContexts #-} module Ser where import Data.Generics import Control.Monad.State import Control.Monad.Writer import qualified Data.Set as Set import Data.Maybe import System.Mem.StableName import System.IO.Unsafe import Debug.Trace type LinkSet = Set.Set Int presents :: (Monad m, MonadState LinkSet m) => Int -> m Bool presents iD = gets (Set.member iD) type SerM r a = StateT LinkSet (WriterT [r] IO) a goRec :: (Data v, Data r) => v -> SerM r () goRec v = do iD <- v `seq` (hashStableName `fmap` liftIO (makeStableName v)) p <- presents iD unless p $ do modify $ Set.insert iD case cast v of Just v0 -> serRi v0 iD Nothing -> continue where serRi v iD = tell [v] >> continue continue = sequence_ $ gmapQ goRec v recUniplate :: (Data v, Data r) => v -> [r] recUniplate v = unsafePerformIO $ execWriterT $ evalStateT (goRec v) Set.empty data Rose = Rose { roseId :: Int, roses :: [Rose] } deriving (Data,Typeable) testRose = let a = Rose 1 [b,c] b = Rose 2 [a,c] c = Rose 3 [a,b] in aРаботаем...
no subject
Date: 2010-02-08 07:12 am (UTC)no subject
Date: 2010-02-08 12:34 pm (UTC)no subject
Date: 2010-02-08 01:09 pm (UTC)no subject
Date: 2010-02-08 03:17 pm (UTC)no subject
Date: 2010-02-08 04:05 pm (UTC)http://haskell.org/haskellwiki/Things_to_avoid - тут не форматирование
Ещё есть соглашения для GHC sources, оттуда тоже можно взять что-нибудь полезное.
Поиск haskell beautifier не дал ничего полезного :(
no subject
Date: 2010-02-08 05:55 pm (UTC)no subject
Date: 2010-02-05 08:14 pm (UTC)no subject
Date: 2010-02-05 09:39 pm (UTC)no subject
Date: 2010-02-05 10:10 pm (UTC)а оно уже работает?
Date: 2010-02-05 10:43 pm (UTC)Re: а оно уже работает?
Date: 2010-02-05 11:14 pm (UTC)Я думаю, мы переживём отсутствие автодокументации. Но даже если оно не скомпилится - идея там достаточно простая, можно и самостоятельно воспроизвести.
Re: а оно уже работает?
Date: 2010-02-05 11:17 pm (UTC)Re: а оно уже работает?
Date: 2010-02-06 12:03 am (UTC)no subject
Date: 2010-02-06 08:29 am (UTC)