封装(序列化)类型类

时间:2016-11-16 19:58:46

标签: haskell

我举一个例子来证明我的问题。

说我想为键值存储构建一个中级API。基于我的API的低级API包含以下三个基本功能:

put :: ByteString -> ByteString -> IO ()
get :: ByteString ->            -> IO (Maybe ByteString)
del :: ByteString ->            -> IO ()

具有明显的语义。为了使我的API更方便,我不仅要传递ByteString,还要传递可序列化的对象。所以我在第一次尝试时定义了

putOp :: Serialize a => ByteString -> a -> IO ()
getOp :: Serialize a => ByteString ->   -> IO (Maybe a)

虽然将cereal硬连接到我的API中,但这种方法很有用。事实上,经过一段时间我遇到binary-serialise-cbor看起来很有希望。但是从cereal更改为它会导致我的API发生重大变化。

问题

是否有一种很好的方法来“封装”我在内部使用的序列化库(避免使用UndecidableInstances)?

常识可能是写自己的类

class Storable a where
   encode :: a -> ByteString
   decode :: ByteString -> Either String a

和newtype

newtype Encode a = Encode { unwrap :: a } deriving (Functor, Eq, Show)

并在putOpgetOp

中隐含地隐瞒了它
putOp :: Storable (Encode a) => ByteString -> a -> IO ()
putOp k x = put k (encode $ Encode x)

getOp :: Storable (Encode a) => ByteString -> IO (Maybe a)
getOp k = do
   x <- (get k) :: IO (Maybe (Encode a))
   return (unwrap <$> x)

我们在哪里

instance Serialize a => Storable (Encode a) where
   encode = S.encode . unwrap
   decode (Encode r) = Encode <$> S.decode r

这看起来不错;虽然

  1. 它增加了开销(或者它是否被优化了?)和
  2. 从外部开始,API现在在其put / get上有一个奇怪的Storable (Encode a)上下文,并为Storable a编写自己的实例,为Storable (Encode a)编写实例。
  3. 有更优雅的解决方案吗?

0 个答案:

没有答案