Database interface

Tom Pledger Tom.Pledger@peace.com
Thu, 21 Aug 2003 11:52:52 +1200


Tim Docker writes:
 :
 | The list being folded over
 | is implied by the DB query, is accessible through the IO monad.
 | Hence a parameter is not required. It would really be:
 | 
 | doquery :: Process -> String -> b -> (b -> IO (b,Bool)) -> IO b
 :
 | One thing that I am unsure about is whether the column value
 | accessing functions that I specified before
 | 
 |    stringv :: Process -> CInt -> IO String
 |    doublev :: Process -> CInt -> IO Double
 |    intv    :: Process -> CInt -> IO Int
 | 
 | should return actions in the IO monad as above, or instead should
 | be in some other DBQuery monad, that trivially extends the IO monad,
 | but is only valid inside the doquery call. This would have the benefit
 | of restricting the column access functions to inside a query via the
 | type system.

How about introducing a Cursor abstract data type?

    doquery :: Process -> String -> b -> (Cursor -> b -> IO (b, Bool))
               -> IO b
    stringv :: Cursor -> CInt -> IO String
    doublev :: Cursor -> CInt -> IO Double
    intv    :: Cursor -> CInt -> IO Int

This achieves the restriction you're after, because doquery is the
only exported producer of Cursor, and stringv etc. are the only
exported consumers of Cursor.

It also has the benefit that the function you pass to doquery can make
other calls to doquery, without mucking up the 'current row' state.
There would be one current row per Cursor, not one per Process.

 | I'd also probably use a typeclass to specify a single colv function.
 | ie:
 | 
 |    class DBCol a where
 |        colv :: DBQuery a
 | 
 |    instance DBCol String where...
 |    instance DBCol Double where...

Good idea.  The user can always use explicit type signatures to
resolve ambiguity, and even then the code size will be about the same
as with stringv etc.

- Tom