开发者

Binary Typing Problem

I am trying to work with the Haskell Bson and I want to save and load them. The saving seems to be no problem, but I get a typing error with the Binary.get functions.

Here's my code:

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances #-}
module Database.Axiom where

import Data.Bson (Document, Field)
import Data.Bson.Binary (putDocument, getDocument)
import Data.Binary as B (Binary(..), decodeFile, encodeFile)
import Control.Monad (liftM2)

instance Binary Document where
    put = putDocument
    get = getDocument

data Collec开发者_StackOverflow中文版tion = Collection {
        collectionName :: ByteString,
        collectionDocs :: [Document]
    }

instance Binary Collection where
    put (Collection name docs) = B.put name >> B.put docs
    get = liftM2 Collection B.get B.get -- < Here is the type error

Which leads to this error:

Database/Axiom.hs:24:39:
    Overlapping instances for Binary [Field]
      arising from a use of `B.get'
    Matching instances:
      instance Binary a => Binary [a] -- Defined in Data.Binary
      instance Binary Document -- Defined at Database/Axiom.hs:13:10-24
    In the third argument of `liftM2', namely `B.get'
    In the expression: liftM2 Collection B.get B.get
    In an equation for `get': get = liftM2 Collection B.get B.get

The problem is that Document is merely a synonym of [Field]. But I need an instance for Binary Document, as there are no functions to serialize a single Field. And moreover, BSON does not export any instances for Binary Field, so I am completely confused why this error happens in the first place.

I tried it with strict type declaration and then to use a self made get method, but a get :: [Document] only works nicely when there's a get :: Document method.

So, anyone can help me, perhaps?


Altough a little subjective, I think the cleanest and most robust way to fix is is to add a newtype for Document. Something like:

import Control.Applicative ((<$>))
import Data.ByteString (ByteString)
import Data.Bson (Document, Field)
import Data.Bson.Binary (putDocument, getDocument)
import Data.Binary as B (Binary(..), decodeFile, encodeFile)
import Control.Monad (liftM2)

newtype CollectionDoc = CollectionDoc {unCollectionDoc :: Document}

instance Binary CollectionDoc where
    put = putDocument . unCollectionDoc
    get = CollectionDoc <$> getDocument

data Collection = Collection {
        collectionName :: ByteString,
        collectionDocs :: [CollectionDoc]
    }

instance Binary Collection where
    put (Collection name docs) = B.put name >> B.put docs
    get = liftM2 Collection B.get B.get

should work. In addition, newtype wrappers are completely optimized away, so there is no overhead at runtime.


Don't define the instance for Document; just call getDocument instead of B.get (which I see no need for qualifying with the B.) in your get definition for Collection.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜