Piszę aplikację, która współdziała z dużym (10-1000 GB) plikiem binarnym odwzorowanym w pamięci, trzymając w zasadzie kilka obiektów, które się do siebie odwołują. Wymyśliłem mechanizm do odczytu/zapisu tych danych, który jest skuteczny, ale brzydki i pełny (imo).Przechowywanie dużych strukturalnych danych binarnych za pomocą Haskella
P: Czy istnieje bardziej elegancki sposób na osiągnięcie tego, co zrobiłem?
Mam typeclass dla danych strukturalnych, z jednej metody, które odczytuje strukturę do typu danych Haskell (DataOp
jest ReaderT
wokół IO
).
class DBStruct a where
structRead :: Addr a -> DataOp a
Aby to bardziej czytelne, mam inny typeclass definiujący co członkowie struktury tam, gdzie:
class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where
offset :: structTy -> valTy -> name -> Int64
Mam kilka funkcji pomocniczych, które wykorzystują metody offset dla elementów konstrukcji odczytu/zapisu, do czytania struktur z zapisanych odniesień i leniwie odraczających odczytów struktury (w celu umożliwienia leniwego odczytu całego pliku).
Problem polega na tym, że wymaga użycia wielu powtórzeń. Na jednej strukturze, najpierw trzeba określić typ Haskell:
data RowBlock = RowBlock {rbNext :: Maybe RowBlock
,rbPrev :: Maybe RowBlock
,rbRows :: [RowTy]
}
Następnie rodzaje name
:
data Next = Next
data Prev = Prev
data Count = Count
newtype Row = Row Int64
Następnie wystąpień dla każdego elementu struktury:
instance StructMem RowBlock (Maybe (Addr RowBlock)) Next where offset _ _ _ = 0
instance StructMem RowBlock (Maybe (Addr RowBlock)) Prev where offset _ _ _ = 8
instance StructMem RowBlock Int64 Count where offset _ _ _ = 16
instance StructMem RowBlock RowTy Row where offset _ _ (Row n) = 24 + n * 8
Następnie strukturę przeczytaj metodę:
instance DBStruct RowBlock where
structRead a = do
n <- elemMaybePtr a Next
p <- elemMaybePtr a Prev
c <- elemRead a Count
rs <- mapM (elemRead a . Row) [0 .. c-1]
return $ RowBlock n p rs
Więc wszystko, co naprawdę osiągnąłem, to ponowne wdrożenie struktur C w dużo bardziej gadatliwy (i powolny) sposób. Byłbym szczęśliwszy, gdyby to było bardziej zwięzłe, przy jednoczesnym zachowaniu bezpieczeństwa typu. Z pewnością jest to powszechnie spotykany problem.
kilka możliwych alternatyw mogę myśleć to:
- Ditch pliki w pamięci i używać
Data.Binary
, pisanieByteStrings
na dysk normalny sposób. - Zastosowanie
deriving Generic
stworzyć ogólne funkcje odczytu i zapisu - Overload Functional References
- Czy coś magicznego z monadycznych soczewek.
EDIT: SSCCE as requested
Czy możesz podać uproszczony, samodzielny przykład, który można skompilować, od czego zacząć? –
@ PetrPudlák Mogę zrobić to później, jeśli uważasz, że to pomoże ludziom znaleźć odpowiedź. Jednak miało to być bardziej architektoniczne pytanie niż pytanie "dlaczego nie jest to mój kod"; Kod, który napisałem, ma być bardziej ilustracją mojej obecnej architektury niż cokolwiek innego. – Dan
Tak, [SSCCE] (http://www.sscce.org/) pomoże mi lepiej zrozumieć obecny projekt. –