使用MultiParamTypeClasses进行灵活的序列化

时间:2014-08-22 14:19:40

标签: haskell serialization typeclass multiparameter

我正在寻找可扩展序列化库的想法。我有以下类型类:

class Monoid m => BuilderS m a where
  cstr :: String -> a -> m

这个想法是人们可以为不同的序列化器/类型对定义实例,如下所示:

import qualified Data.Serialize.Builder as B

instance Serialize a => BuilderS B.Builder a where
  cstr _ = B.fromByteString . encode

一个用法示例:

sfPut :: (BuilderS a b, Monoid a) => WriterT a Identity ()
sfPut = do
  tell $ cstr "version" (5 :: Int)
  -- ...
  return ()

然而,事实证明a的类型需要专门化:

Could not deduce (BuilderS a Int) arising from a use of `cstr'
    from the context (BuilderS a b, Monoid a)
      bound by the type signature for
                 sfPut :: (BuilderS a b, Monoid a) => WriterT a Identity ()

,即以下类型签名没有问题:

sfPut :: WriterT B.Builder Identity ()

我是否有一种明显的方法可以解决这个问题?

1 个答案:

答案 0 :(得分:1)

好吧,如果我使用你给出的类型类,那么使用GHCi检查类型:

> :t tell $ cstr "version" (5 :: Int)
tell $ cstr "version" (5 :: Int) :: (MonadWriter s m, BuilderS s Int) => m ()

所以看起来您需要指定BuilderS a Int,而不是BuilderS a b。如果我有

sfPut :: BuilderS a Int => WriterT a Identity ()
sfPut = tell $ cstr "version" (5 :: Int)

工作正常。请注意,您也需要FlexibleContexts5上的类型签名不是可选的。


为了进一步说明,您提供的类型签名sfPut

BuilderS m a => WriterT m Identity ()

但你有一个tell $ cstr "version (5 :: Int)这个词,其类型为

BuilderS m Int => WriterT m Identity ()

类型系统无法统一aInt,因此它会给您一个错误。此外,如果您不使用5 :: Int的类型签名,则可以使用

tell $ cstr "version" 5 :: (Num a, BuilderS m a) => WriterT m Identity ()

但由于a没有出现在WriterT m Identity ()中,因此类型系统无法知道Num用于5的哪个实例,并且给你另一个错误。具体来说,你得到

> let sfPut = tell $ cstr "version" 5
Could not deduce (BuilderS s a0)
  arising from the ambiguity check for ‘sfPut’
from the context (BuilderS s a, MonadWriter s m, Num a)
  bound by the inferred type for ‘sfPut’:
             (BuilderS s a, MonadWriter s m, Num a) => m ()
  at <interactive>:20:5-35
The type variable ‘a0’ is ambiguous
When checking that ‘sfPut’
  has the inferred type ‘forall (m :: * -> *) s a.
                         (BuilderS s a, MonadWriter s m, Num a) =>
                         m ()’
Probable cause: the inferred type is ambiguous

但是,如果您使用单形文字(或类型不是多态的值),那么您可以轻松地执行

> let sfPut = tell $ cstr "version" "I'm not polymorphic"
> :t sfPut
sfPut :: (BuilderS s [Char], MonadWriter s m) => m ()