-- This Source Code Form is subject to the terms of the Mozilla Public -- License, v. 2.0. If a copy of the MPL was not distributed with this -- file, You can obtain one at http://mozilla.org/MPL/2.0/. {-# LANGUAGE CPP #-} {-# LANGUAGE TypeFamilies #-} module Database.CQL.Protocol.Record ( Record (..) , TupleType , recordInstance ) where import Control.Monad import Language.Haskell.TH typeSynDecl :: Name -> [Type] -> Type -> Dec #if __GLASGOW_HASKELL__ < 708 typeSynDecl = TySynInstD #else typeSynDecl x y z = TySynInstD x (TySynEqn y z) #endif type family TupleType a -- | Record/Tuple conversion. -- For example: -- -- @ -- data Peer = Peer -- { peerAddr :: IP -- , peerRPC :: IP -- , peerDC :: Text -- , peerRack :: Text -- } deriving Show -- -- recordInstance ''Peer -- -- map asRecord \<$\> performQuery "SELECT peer, rpc_address, data_center, rack FROM system.peers" -- @ -- -- The generated type-class instance maps between record and tuple constructors: -- -- @ -- type instance TupleType Peer = (IP, IP, Text, Text) -- -- instance Record Peer where -- asTuple (Peer a b c d) = (a, b, c, d) -- asRecord (a, b, c, d) = Peer a b c d -- @ -- class Record a where asTuple :: a -> TupleType a asRecord :: TupleType a -> a recordInstance :: Name -> Q [Dec] recordInstance n = do i <- reify n case i of TyConI d -> start d _ -> fail "expecting record type" start :: Dec -> Q [Dec] start (DataD _ tname _ cons _) = do unless (length cons == 1) $ fail "expecting single data constructor" tt <- tupleType (head cons) at <- asTupleDecl (head cons) ar <- asRecrdDecl (head cons) return [ typeSynDecl (mkName "TupleType") [ConT tname] tt , InstanceD [] (ConT (mkName "Record") $: ConT tname) [ FunD (mkName "asTuple") [at] , FunD (mkName "asRecord") [ar] ] ] start _ = fail "unsupported data type" tupleType :: Con -> Q Type tupleType c = do let tt = types c return $ foldl1 ($:) (TupleT (length tt) : types c) where types (NormalC _ tt) = map snd tt types (RecC _ tt) = map (\(_, _, t) -> t) tt types _ = fail "record and normal constructors only" asTupleDecl ::Con -> Q Clause asTupleDecl c = case c of (NormalC n t) -> go n t (RecC n t) -> go n t _ -> fail "record and normal constructors only" where go n t = do vars <- replicateM (length t) (newName "a") return $ Clause [ConP n (map VarP vars)] (body vars) [] body = NormalB . TupE . map VarE asRecrdDecl ::Con -> Q Clause asRecrdDecl c = case c of (NormalC n t) -> go n t (RecC n t) -> go n t _ -> fail "record and normal constructors only" where go n t = do vars <- replicateM (length t) (newName "a") return $ Clause [TupP (map VarP vars)] (body n vars) [] body n v = NormalB $ foldl1 ($$) (ConE n : map VarE v) ($$) :: Exp -> Exp -> Exp ($$) = AppE ($:) :: Type -> Type -> Type ($:) = AppT