我正在寻找可扩展序列化库的想法。我有以下类型类:
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 ()
我是否有一种明显的方法可以解决这个问题?
答案 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)
工作正常。请注意,您也需要FlexibleContexts
,5
上的类型签名不是可选的。
为了进一步说明,您提供的类型签名sfPut
是
BuilderS m a => WriterT m Identity ()
但你有一个tell $ cstr "version (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
但是,如果您使用单形文字(或类型不是多态的值),那么您可以轻松地执行
> let sfPut = tell $ cstr "version" "I'm not polymorphic"
> :t sfPut
sfPut :: (BuilderS s [Char], MonadWriter s m) => m ()