大規模な (10 ~ 1000 GB) メモリ マップされたバイナリ ファイルとやり取りするアプリケーションを作成しています。このファイルは、基本的に、相互に参照する一連のオブジェクトを保持しています。私はこのデータを読み書きするメカニズムを思いつきました。これは効果的ですが、醜くて冗長です(imo)。
Q: 私が行ったことを達成するためのよりエレガントな方法はありますか?
構造化データの型クラスがあり、構造を Haskell データ型 ( DataOp
is a ReaderT
around IO
) に読み込む 1 つのメソッドがあります。
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
構造要素の読み取り/書き込み、格納された参照からの構造の読み取り、および構造読み取りの遅延遅延 (ファイル全体の遅延読み取りを可能にするため) にオフセット メソッドを使用するヘルパー関数がいくつかあります。
これの問題は、使用するのに多くの繰り返しが必要なことです。1 つの構造体については、まず 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
汎用の読み取りおよび書き込み関数を作成するために使用します- 関数参照のオーバーロード
- モナドレンズで魔法のようなことをしましょう。
編集:要求に応じてSSCCE