S. Alexander Jacobson writes:
> > class ODBCTransaction transaction where
> >  odbcSelect::transaction -> SQLSelect -> [ODBCResult]
> >  odbcChange::transaction -> SQLChange -> IO()
> >  [...]

I am afraid that this cannot work properly.

A transaction may consist of several queries and updates. It is
necessary to specify
- the order of the updates and
- for every query to which database state it refers, i.e., the initial
  state of the transaction, the final state, or some intermediate state
  between two updates.
However, we need (in theory) not specify an order between queries that
refer to the same state.

The problem is that your function odbcSelect does not allow to specify
when the query is to be evaluated within the transaction. This can
easily be fixed by moving odbcSelect in the IO monad:

> odbcSelect :: transaction -> SQLSelect -> IO [ODBCResult]

But using this function also specifies an order between several queries
to the same state, which you might not want.  So I could think of
something like

> odbcMapSelect :: transaction -> [SQLSelect] -> IO [[ODBCResult]]

which allows you to specify several queries and to get several results
at once. The order in which the queries are evaluated is not specified.
(The implementation might even interleave the evaluation of several
queries.) I used a list only to make it simple to find out which answer
corresponds to which query.

Still this will not be easy to implement (if at all possible) with ODBC
and other typical database interfaces.

Sometimes a more flexible serialization of operations within a
transaction is possible. For example, if a query does not refer to those
parts of the database that are affected by some update, then it's (in
theory) not necessary to specify whether the query refers to the state
before or after the update. But these things are not so easy to check
and to implement for the general case. Maybe the programmer should get a
means to promise that it is irrelevant whether a particular query is
evaluated before or after some particular update. (This starts to remind
me to the discussion of nondeterminism in the thread on exception
handling.)

Full integration of database access in a *lazy* language is certainly a
larger research project. Just consider all the work on integrating
Prolog and (SQL-based) databases. More "ad-hoc" solutions like porting
JDBC to Haskell will probably always sacrifice some of the laziness.

Another thing is, of course, read-only databases (e.g., on a CD-ROM).
There you don't need transactions at all and query evaluation might be
done outside the IO monad (and lazily) without semantic problems.

Regards,

Heribert.


PS: I find it very useful that this discussion about database access
from Haskell programs has been started. A well-designed and (more or
less) standardized DB interface will make Haskell more usable in the
real world. (I still have to have a closer look on the "St. Peterburg
interface".)

PPS: Another problem: What if your query happens to be evaluated at a
point of time when the transaction is already committed or aborted? Note
that you don't have much control over this time if you are outside the
IO monad. (I think a similar question has been asked in this thread
already with database connections instead of transactions.)


Reply via email to