Haskell 使用MultiparamTypeClass进行灵活的序列化

Haskell 使用MultiparamTypeClass进行灵活的序列化,haskell,serialization,typeclass,multiparameter,Haskell,Serialization,Typeclass,Multiparameter,我正在考虑一个可扩展序列化库的想法。我有以下typeclass: 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 _

我正在考虑一个可扩展序列化库的想法。我有以下typeclass:

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 ()
i、 e、以下类型签名工作正常:

sfPut :: WriterT B.Builder Identity ()

有没有一个明显的方法我没有解决这个问题?

好吧,如果我使用您给出的typeclass,那么使用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)
它很好用。请注意,您也需要
FlexibleContexts
,并且
5
上的类型签名不是可选的


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

BuilderS m a => WriterT m Identity ()
但是您有术语
tell$cstr”版本(5::Int)
,它的类型是

BuilderS m Int => WriterT m Identity ()
类型系统无法统一
a
Int
,因此它给了您一个错误。此外,如果您从
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

因此,您必须在
BuilderS
中指定
m
a
,但无法帮助类型系统同时推断这两者?@PhilipKamenarsky问题是您说它需要是
BuilderS mb
,但
cstr”版本(5::Int)
具有类型
BuilderS m Int
。在
5
(至少在GHC 7.8中)上需要类型签名,因为否则它无法确定使用
Num
的哪个实例,因为
b
术语仅出现在类型签名的约束侧。
> let sfPut = tell $ cstr "version" "I'm not polymorphic"
> :t sfPut
sfPut :: (BuilderS s [Char], MonadWriter s m) => m ()