我正在編寫一個應用程序,它與大型(10-1000 GB)內存映射二進制文件進行交互,基本上擁有一堆相互引用的對象。我想出了一種讀取/寫入這種數據的機制,這種數據是有效的,但是醜陋而冗長(imo)。用Haskell存儲大型結構化二進制數據
問:有沒有更優雅的方式來實現我所做的?
我對結構化數據類型類一個,其中一個方法,它讀取結構成一個Haskell數據類型(DataOp
是ReaderT
周圍IO
)。
class DBStruct a where
structRead :: Addr a -> DataOp a
爲了使這個更具可讀性,我有另一種類型類,它定義了結構成員去的地方:
class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where
offset :: structTy -> valTy -> name -> Int64
我有使用失調的方法讀/寫結構元素幾個輔助功能,用於從存儲的引用中讀取結構,以及用於懶惰地延遲結構讀取(以允許整個文件的懶讀)。
問題在於它涉及很多重複使用。對於一個結構,我首先必須定義Haskell的類型:
data RowBlock = RowBlock {rbNext :: Maybe RowBlock
,rbPrev :: Maybe RowBlock
,rbRows :: [RowTy]
}
然後name
類型:
data Next = Next
data Prev = Prev
data Count = Count
newtype Row = Row Int64
然後,對於每個結構構件的實例:
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
然後,結構讀取方法:
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
所以我真正完成的是以更加冗長(緩慢)的方式重新實現C結構。如果這更簡潔,同時保持型號安全,我會更高興。當然這是一個經常遇到的問題。
我能想到的一些可能的替代方案是:
- 溝內存映射文件,並使用
Data.Binary
,寫ByteStrings
到磁盤的正常方式。 - 使用
deriving Generic
創建通用的讀寫功能 - Overload Functional References
- 做些什麼神奇與一元的鏡片。
您能否提供一個可以編譯的簡化自包含示例? –
@PetrPudlák今天晚些時候我可以做到這一點,如果你認爲它會幫助人們提出答案。但是,這意味着更多的是一個架構問題,而不是爲什麼 - 我的代碼工作問題;我發佈的代碼意味着比其他任何內容更能說明我目前的架構。 – Dan
是的,[SSCCE](http://www.sscce.org/)將幫助我更好地理解您當前的設計。 –