On Jan 30, 2008, at 19:01 , Justin Bailey wrote:

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.

No, there is no existing way to achieve this. Indeed, it's one of the most common complaints about HaskellDB :-)

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?

I doubt that there is. In general, manipulating record types with Haskell's roundabout type level programming is messy, there's no avoiding that. 

 2) If not 1, would it make more sense for the Query monad to pack
around the original Expr values?

Hmm, wouldn't it need to carry those through arbitrary query computations?

 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.

The best way to do it would be to add powerful record types to Haskell...

You may want to look at HList, http://homepages.cwi.nl/~ralf/HList/ I'm not sure how much HList can do now, perhaps they have added things since they wrote the paper. There might be some nice record manipulation stuff there that you could use. Porting HaskellDB to use HList instead of its own records would be nice, if the HList code is in good shape. 

Thanks for any and all thoughts!

Justin

p.s. Sorry for the spam on cafe - I accidently sent this there first ...

/Björn