functional database queries

At http://www.haskell.org/hawiki/HaskellDbTutorial it is described, how database queries can be modelled with a monad. However, I wonder if this is also possible without monads. Say, writing "DB.map col1 $ DB.filter (\row -> col2 row == 10+2) myTable" for "SELECT col1 FROM MyTable where col2 = 10+2"

Henning Thielemann wrote:
At http://www.haskell.org/hawiki/HaskellDbTutorial it is described, how database queries can be modelled with a monad. However, I wonder if this is also possible without monads. Say, writing
"DB.map col1 $ DB.filter (\row -> col2 row == 10+2) myTable"
for
"SELECT col1 FROM MyTable where col2 = 10+2"
If and only if the database is a purely functional immutable data structure, this can be done. This is because the $ operator, function application, is used for control and/or dataflow. Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back. The >>= operator generalizes from function application to these cases. Thus the monadic way subsumes the functional way and covers other uses. You can also make it an arrow. You can also make it an applicative.

Henning Thielemann wrote:
At http://www.haskell.org/hawiki/HaskellDbTutorial it is described, how database queries can be modelled with a monad. However, I wonder if this is also possible without monads. Say, writing
"DB.map col1 $ DB.filter (\row -> col2 row == 10+2) myTable"
for
"SELECT col1 FROM MyTable where col2 = 10+2"
Judging from the papers mentioned in the Haddocks, the monad is used like a list comprehension. This way, joins can be expressed as query = do x <- table languages y <- table programmers restrict (language ! paradigm .==. constant PurelyFunctional) ... Seems to be the main reason for a monadic interface. Of course, the query is compiled to SQL, so filter or its monadic equivalent cannot use arbitrary functions. This is prevented by giving x and y opaque types so that the programmer cannot really access them. I think this is why the monad feels a bit ill, i.e. despite the monad, subsequent actions cannot depend on the contents of x and y (because this contents is just a dummy). Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base. Regards, apfelmus

apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base.
The same clock read twice, each reading atomic, can give two different results. (Cf. the monadic type signature of Data.Time.Clock.getCurrentTime.) The same SELECT to the same database issued twice, each time atomic, can give two different results. These are not referentially transparent. These are not purely functional. The notation is a beauty bonus.

Albert Y. C. Lai wrote:
apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base.
The same clock read twice, each reading atomic, can give two different results. (Cf. the monadic type signature of Data.Time.Clock.getCurrentTime.)
The same SELECT to the same database issued twice, each time atomic, can give two different results.
Yeah, of course. That's why the function that executes the query is in the IO-monad: query :: GetRec er vr => Database -> Query (Rel er) -> IO [Record vr] Hennings' question is whether the query type 'Query (Rel el)' really has to be a monad, not whether the function 'query' has to be in the IO-monad. In other words, 'Query a' just assembles a valid SQL-string, it does not query or execute anything. Regards, apfelmus

On Feb 21, 2007, at 20:47 , apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base.
The same clock read twice, each reading atomic, can give two different results. (Cf. the monadic type signature of Data.Time.Clock.getCurrentTime.)
The same SELECT to the same database issued twice, each time atomic, can give two different results.
Yeah, of course. That's why the function that executes the query is in the IO-monad:
query :: GetRec er vr => Database -> Query (Rel er) -> IO [Record vr]
Hennings' question is whether the query type 'Query (Rel el)' really has to be a monad, not whether the function 'query' has to be in the IO-monad. In other words, 'Query a' just assembles a valid SQL-string, it does not query or execute anything.
Regards, apfelmus
This is correct, the Query monad is just used to construct the query. Running the query is done in IO. If we look in the source code (http://darcs.haskell.org/haskelldb/src/ Database/HaskellDB/Query.hs), we see that the Query monad is a state monad, whose state is the current query and an Int used to generate fresh field names. It would certainly possible to do this without a monad, though it would probably require reworking the PrimQuery type. /Björn

On Wed, 21 Feb 2007 apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base.
The same clock read twice, each reading atomic, can give two different results. (Cf. the monadic type signature of Data.Time.Clock.getCurrentTime.)
The same SELECT to the same database issued twice, each time atomic, can give two different results.
Yeah, of course. That's why the function that executes the query is in the IO-monad:
query :: GetRec er vr => Database -> Query (Rel er) -> IO [Record vr]
Hennings' question is whether the query type 'Query (Rel el)' really has to be a monad, not whether the function 'query' has to be in the IO-monad.
Right.
In other words, 'Query a' just assembles a valid SQL-string, it does not query or execute anything.
Of course, instead of the DSEL approach "don't execute anything, only construct a program in a foreign language which does that" it would be nice to have a database where Haskell is the native query language, allowing to access the database content with 'map', 'filter' and so on.

Henning Thielemann wrote:
In other words, 'Query a' just assembles a valid SQL-string, it does not query or execute anything.
Of course, instead of the DSEL approach "don't execute anything, only construct a program in a foreign language which does that" it would be nice to have a database where Haskell is the native query language, allowing to access the database content with 'map', 'filter' and so on.
Yes, though this is most probably infeasible: unrestricted lambda-calculus queries are disallowed for reasons of computability and efficiency. Of course, you can still conceive a DSEL offering 'map' and 'filter', it's just that these will not be the known Haskell functions then. As far as I see, the main point for 'Query a' being a monad is that you need 'concatMap' == '>>=' to express table joins. After all, accessing databases just means general list comprehensions. Regards, apfelmus

On Feb 22, 2007, at 14:56 , Henning Thielemann wrote:
On Wed, 21 Feb 2007 apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
apfelmus@quantentunnel.de wrote:
Albert Y. C. Lai wrote:
If and only if the database is a purely functional immutable data structure, this can be done. [...] Many interesting databases are not purely functional immutable; most reside in the external world and can spontaneously change behind your program's back.
I don't think this is the problem because SQL requests are emitted atomically anyway. The (Query a) monad here has nothing to do with mutability of the data base.
The same clock read twice, each reading atomic, can give two different results. (Cf. the monadic type signature of Data.Time.Clock.getCurrentTime.)
The same SELECT to the same database issued twice, each time atomic, can give two different results.
Yeah, of course. That's why the function that executes the query is in the IO-monad:
query :: GetRec er vr => Database -> Query (Rel er) -> IO [Record vr]
Hennings' question is whether the query type 'Query (Rel el)' really has to be a monad, not whether the function 'query' has to be in the IO-monad.
Right.
In other words, 'Query a' just assembles a valid SQL-string, it does not query or execute anything.
Of course, instead of the DSEL approach "don't execute anything, only construct a program in a foreign language which does that" it would be nice to have a database where Haskell is the native query language, allowing to access the database content with 'map', 'filter' and so on.
Have you seen CoddFish (http://wiki.di.uminho.pt/twiki/bin/view/ Research/PURe/CoddFish)? /Björn
participants (4)
-
Albert Y. C. Lai
-
apfelmus@quantentunnel.de
-
Bjorn Bringert
-
Henning Thielemann