{-# LANGUAGE DeriveGeneric, OverloadedStrings #-} --this example shows how to implement a non-generics-defined Tupleable instance which is required in scenarios where Haskell-side types are not supported server-side or where one wishes to represent a nested relation import ProjectM36.Client import ProjectM36.Tuple import ProjectM36.Tupleable import ProjectM36.Relation import ProjectM36.Atom import ProjectM36.Attribute as A import qualified Data.Set as S import qualified Data.Map as M import Data.Text (Text) import GHC.Generics import Data.Proxy --in this contrived example, we wish to represent one relation with a nested relation of comments data Blog = Blog { title :: Text, comments :: S.Set Comment } deriving (Show) data Comment = Comment { authorName :: Text, comment :: Text } deriving (Eq, Show, Generic, Ord) instance Tupleable Comment instance Tupleable Blog where toTuple blogentry = mkRelationTupleFromMap (M.fromList [("title", TextAtom (title blogentry)), ("comments", RelationAtom relFromComments)]) where commentTuples = map toTuple (S.toList (comments blogentry)) relFromComments = case mkRelationFromTuples (toAttributes (Proxy :: Proxy Comment)) commentTuples of Left err -> error (show err) Right rel -> rel fromTuple tupIn = do titleAtom <- atomForAttributeName "title" tupIn commentsAtom <- atomForAttributeName "comments" tupIn commentsRel <- relationForAtom commentsAtom comments' <- mapM fromTuple (tuplesList commentsRel) pure Blog { title = atomToText titleAtom, comments = S.fromList comments'} toAttributes _ = A.attributesFromList [Attribute "title" TextAtomType, Attribute "comments" $ RelationAtomType (toAttributes (Proxy :: Proxy Comment))] main :: IO () main = do let exampleBlog = Blog { title = "Cat Pics", comments = S.fromList [Comment {authorName = "Steve", comment = "great"}, Comment {authorName = "Bob", comment = "enough"} ]} print (toTuple exampleBlog) print (fromTuple (toTuple exampleBlog) :: Either RelationalError Blog)