Does DB.getTables use 'unsafeInterleaveIO'?<br><br>I would think that if an unsafe operation was *not* used, DB.disconnect could *not* execute before DB.getTables has returned every row.<br><br>Either way, by the <a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment">
Principle of Least Surprise</a>, I think Tim's original code ought to be made to work, despite not leveraging laziness.&nbsp; <br><br>If you are going to tuck away an unsafeInterleaveIO, it seems reasonable that an explicit disconnect should force those deferred operations to be evaluated.&nbsp; Maybe the same should be done for hGetContents/hClose too?
<br><br>Thanks,<br>Greg<br><br><br><div><span class="gmail_quote">On 10/9/06, <b class="gmail_sendername">John Goerzen</b> &lt;<a href="mailto:jgoerzen@complete.org">jgoerzen@complete.org</a>&gt; wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
On Mon, Oct 09, 2006 at 04:01:02PM -0600, Tim Smith wrote:<br>&gt; main =<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;do<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;dbh &lt;- connectODBC &quot;DSN=test&quot;<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;res &lt;- DB.getTables dbh<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;-- print (show ((concat . intersperse &quot;, &quot;) res))
<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;DB.disconnect dbh<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp;print (show ((concat . intersperse &quot;, &quot;) res))<br>&gt;<br>&gt; Am I just expecting the wrong thing from Haskell?&nbsp;&nbsp;Is there a<br><br>Yes.&nbsp;&nbsp;Remember the Haskell mantra: no computation is performed before
<br>its result is demanded.&nbsp;&nbsp;Since you are not demanding the list of tables<br>until the print statement, the code to get the list of tables is not<br>executed until then.<br><br>Actually, follow that logic through.&nbsp;&nbsp;Think about when the connection to
<br>the database is established.&nbsp;&nbsp;If I understand things properly, it won't<br>happen until your call to disconnect, since nothing demands the handle<br>until then.&nbsp;&nbsp;Note that this is normally not the case, since the first
<br>use of it will demand that the connection happens.<br><br>I think the easiest way around this is to add this line after the call<br>to getTables:<br><br>return $ seq res res<br><br>though you may also be able to say:<br>
<br>evaluate res<br><br>(provided you have imported Control.Exception)<br><br>But the very best way is to simply not disconnect until after you've<br>printed.<br><br>&gt; technical reason why HDBC can't synchronize the IO so that everything
<br>&gt; is resolved before the disconnect?&nbsp;&nbsp;Or is this a bug in HDBC?<br><br>It's a Feature of Haskell, not a bug.<br><br>This is the same feature that lets you process infinite lists, treat<br>multi-GB files as strings, and, in fact, treat multi-GB SQL result sets
<br>as simple lists.&nbsp;&nbsp;Haskell only loads each line of the file, or row of<br>result, into RAM when it is demanded.&nbsp;&nbsp;(Note that some databases are<br>less lazy than Haskell in this respect, so this only works if your<br>database API can return partial results!)
<br><br>I have tried to put warnings into the HDBC docs where I think people are<br>particularly likely to run afoul of this -- quickQuery springs to mind.<br><br>Note that the API docs for getTables, at<br><a href="http://darcs.complete.org/hdbc/doc/Database-HDBC.html#v%3AgetTables">
http://darcs.complete.org/hdbc/doc/Database-HDBC.html#v%3AgetTables</a><br>mention that the data is returned in the same manner as fetchAllRows.<br>Click on the link to fetchAllRows and you see:<br><br>&nbsp;&nbsp;Lazily fetch all rows from an executed Statement.
<br><br>&nbsp;&nbsp;You can think of this as hGetContents applied to a database result set.<br><br>&nbsp;&nbsp;The result of this is a lazy list, and each new row will be read,<br>&nbsp;&nbsp;lazily, from the database as the list is processed.<br><br>
&nbsp;&nbsp;When you have exhausted the list, the Statement will be finished.<br><br>&nbsp;&nbsp;Please note that the careless use of this function can lead to some<br>&nbsp;&nbsp;unpleasant behavior. In particular, if you have not consumed the entire
<br>&nbsp;&nbsp;list, then attempt to finish or re-execute the statement, and then<br>&nbsp;&nbsp;attempt to consume more elements from the list, the result will almost<br>&nbsp;&nbsp;certainly not be what you want.<br><br>&nbsp;&nbsp;But then, similar caveats apply with hGetContents.
<br><br>&nbsp;&nbsp;Bottom line: this is a very convenient abstraction; use it wisely.<br><br>-- John<br><br>_______________________________________________<br>Haskell-Cafe mailing list<br><a href="mailto:Haskell-Cafe@haskell.org">
Haskell-Cafe@haskell.org</a><br><a href="http://www.haskell.org/mailman/listinfo/haskell-cafe">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br></blockquote></div><br>