All,
project doesn't easily let me add a few columns to an existing query
(or take a few columns away). Instead, each use of project requires me
to build the entire list of columns I'd like to pass on by hand.
Before I go further, if there is a way to do that, please let me know.
An example of what I'd like to do is below. Imagine I want to add a
"selected" boolean column to my query.
select_customers = do
cust <- table customers
project ( (selectedField <<- False) # cust)
So I get all the columns in my customers table, plus the "selected"
column. Unfortunately the code above doesn't work.
cust has type "Rel r", and project wants a "Record r", so I originally
tried something like:
select_customers = do
let toRec :: Rel r -> Record r
toRec _ = undefined
...
project (unRel cust)
But project needs to recover the "Expr" values associated with 'r' and
this fails. I then came up with this solution, which lets me add
columns at the head of a query:
class RecoverExpr a b where
toExpr :: a -> b
instance RecoverExpr RecNil RecNil where
toExpr rec = RecNil
instance (FieldTag f, RecoverExpr r r) => RecoverExpr (RecCons f
(Expr b) r) (RecCons f (Expr b) r) where
toExpr ~rec@(RecCons _ r) = RecCons (attribute . fieldName $
recField $ rec) (toExpr r)
recField :: RecCons f a b -> f
recField _ = undefined
rest :: (RecoverExpr r r) => Rel r -> r
rest qry =
let unRel :: Rel r -> r
unRel _ = undefined
in toExpr . unRel $ qry
Then I can write:
select_customers = do
cust <- table customers_tbl
...
project (selectField <<- False # (rest cust))
And it actually works. However, it seems over-engineered. My questions are:
1) Is there a way to make project behave like I want without these tricks?
2) If not 1, would it make more sense for the Query monad to pack
around the original Expr values?
3) If not 2, how can the solution above be better? I'd like to extend
it to adding/removing arbitrary columns and more control over column
order.
Thanks for any and all thoughts!
Justin