0

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

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 ()

有没有明显的方法可以解决这个问题?

4

1 回答 1

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 ()

但是你有 term tell $ cstr "version (5 :: Int),它有类型

BuilderS m Int => WriterT m Identity ()

类型系统无法统一aand 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 ()
于 2014-08-22T14:26:47.470 回答