8
votes

I have two simple data types in Haskell:

data Ticket = Ticket {
  tbody :: String,
  tauthor :: String,
  tcomments :: [TicketComment]
}
data TicketComment = TicketComment {
  tcbody :: String,
  tcauthor :: String
}

Ignoring for a moment the lack of timestamps, and the use of strings vs. bytestrings, I simply want to store the comments in MongoDB nested in their tickets.

Up until now I have been using a rather simple instance to store data:

class MongoIO a where
  transout :: a -> [Field]
  transin :: [Field] -> (Maybe a)

The implementation then looks something like this:

instance MongoIO Ticket where
  transout a = [("body" =: tbody a),
               ("author" =: tauthor a),
               ("comments" =: tcomments a)]
  transin a = case (,,) <$> look "body" a
                         <*> look "author" a
                         <*> look "comments" a of
                 Nothing -> Nothing
                 Just (bo,au,co) ->
                   Just $ Ticket (typed bo) (typed au) (typed co)

As would be expected, this breaks down at ("comments" =: tcomments a). I'm confident that I'm getting into an area of Haskell types where my own knowledge is lacking so I'm excited to hear about how others would approach this.

2

2 Answers

8
votes

You have to translate embedded documents also. So

instance MongoIO Ticket where
  transout t = [
    "body" =: tbody t,
    "author" =: tauthor t,
    "comments" =: map transout (tcomments t) ]
  transin d = Ticket
    <$> lookup "body" d
    <*> lookup "author" d
    <*> (mapM transin =<< lookup "comments" d)

plus similar instance for TicketComment.

Also I would use the type synonym Document for [Field].

2
votes

It looks like you just have the transin and transout implementations reversed in your instance. Your transin takes a Ticket and returns a list of Fields; but that's transout's type.