[Haskell-cafe] Re: Strictness, order of IO operations: NewCGI & HDBC

John Goerzen jgoerzen at complete.org
Mon Oct 9 18:13:20 EDT 2006


On Mon, Oct 09, 2006 at 04:01:02PM -0600, Tim Smith wrote:
> main =
>    do
>    dbh <- connectODBC "DSN=test"
>    res <- DB.getTables dbh
>    -- print (show ((concat . intersperse ", ") res))
>    DB.disconnect dbh
>    print (show ((concat . intersperse ", ") res))
> 
> Am I just expecting the wrong thing from Haskell?  Is there a

Yes.  Remember the Haskell mantra: no computation is performed before
its result is demanded.  Since you are not demanding the list of tables
until the print statement, the code to get the list of tables is not
executed until then.

Actually, follow that logic through.  Think about when the connection to
the database is established.  If I understand things properly, it won't
happen until your call to disconnect, since nothing demands the handle
until then.  Note that this is normally not the case, since the first
use of it will demand that the connection happens.

I think the easiest way around this is to add this line after the call
to getTables:

return $ seq res res

though you may also be able to say:

evaluate res

(provided you have imported Control.Exception)

But the very best way is to simply not disconnect until after you've
printed.

> technical reason why HDBC can't synchronize the IO so that everything
> is resolved before the disconnect?  Or is this a bug in HDBC?

It's a Feature of Haskell, not a bug.

This is the same feature that lets you process infinite lists, treat
multi-GB files as strings, and, in fact, treat multi-GB SQL result sets
as simple lists.  Haskell only loads each line of the file, or row of
result, into RAM when it is demanded.  (Note that some databases are
less lazy than Haskell in this respect, so this only works if your
database API can return partial results!)

I have tried to put warnings into the HDBC docs where I think people are
particularly likely to run afoul of this -- quickQuery springs to mind.

Note that the API docs for getTables, at
http://darcs.complete.org/hdbc/doc/Database-HDBC.html#v%3AgetTables
mention that the data is returned in the same manner as fetchAllRows.
Click on the link to fetchAllRows and you see:

  Lazily fetch all rows from an executed Statement.

  You can think of this as hGetContents applied to a database result set.

  The result of this is a lazy list, and each new row will be read,
  lazily, from the database as the list is processed.

  When you have exhausted the list, the Statement will be finished.

  Please note that the careless use of this function can lead to some
  unpleasant behavior. In particular, if you have not consumed the entire
  list, then attempt to finish or re-execute the statement, and then
  attempt to consume more elements from the list, the result will almost
  certainly not be what you want.

  But then, similar caveats apply with hGetContents.

  Bottom line: this is a very convenient abstraction; use it wisely. 

-- John



More information about the Haskell-Cafe mailing list