I'm interested in becoming more familiar with functional programming as a paradigm, so I'm trying to introduce a more functional style to some of my projects. I'm struggling to understand how to handle side effects with a database.
I have some functions that kind of look like this:
db query
+
db query |
+ |
| v
| +--------->a()
v |
f(type)+--+
|
+--------->b()
The trouble is that both f
and a
are non-pure functions because they need to do database queries. I've seen some functional projects that work by having all the state in a single place and the rest of the application takes bits and pieces of state as function parameters. I can replicate something like that here by putting all the queries in f
for example, but since b
doesn't need the database queries used by a
, this would be really inefficient.
Is there a pattern for handling database access in functional programs?
f
as a parameter? In most of the cases it is not significant, especially in cases where you can load data asynchronously. – Fabio Feb 06 '21 at 19:14SELECT
statement to be a side effect, but anINSERT
certainly would. – Greg Burghardt Feb 06 '21 at 20:03SELECT
statement is often a pure function of the database, and the transaction control system prevents "side effects" from one transaction being interwoven with the processing being performed in another. – Steve Feb 06 '21 at 21:58SELECT
statement returns data from the file system, which can be updated between select statements. – Fabio Feb 06 '21 at 22:03SELECT COUNT(*) FROM MyTable
returns99
. Next week, same query(with same arguments as you said)SELECT COUNT(*) FROM MyTable
will return120
. With pure functionsum(12, 30)
returns42
today and next week it will also return42
. With pure function I can save final result and never call this function anymore for arguments12
and30
, but I can not do it for SQL statements, I need to execute actual sql statement again. – Fabio Feb 07 '21 at 21:28MyTable
represents 99 rows in the first case, and 120 rows in the second. It's no different from havingsum(a, b)
, where the values of 'a' and 'b' are changed between calls - being 12 and 30 the first time, then 40 and 55 the next week. Similarly, if you provided a literal table in the from clause (in the same way as you provided literal integers to the sum operator, and instead of providing a table reference) then the result would always be the same and could never change until the hard-coded literals were changed. – Steve Feb 07 '21 at 21:39SELECT SUM(i) FROM (VALUES (12), (30)) AS x(i)
. This will always return a scalar value of 42, but it does so by constructing an input table, then summing thei
column. Similarly, aCOUNT(*)
would always return a value of 2 (that being the count of rows within the table literal). These results will not change week to week, because the inputs are hard-coded. – Steve Feb 07 '21 at 21:48"SELECT COUNT(*) FROM MyTable"
string to the database and receives different results at different time, which is the case for OP's example. – Fabio Feb 07 '21 at 22:31