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
.
精彩评论