pedantically, talking to the database will involve sending a request or some kind of IPC, all of which are usually considered side effects.
[handwavy analogy alert]
people often call it "side-effects" as a shorthand. in reality an expression being of type `IO Foo` mostly just tells the compiler that order of execution matters (which isn't the case with pure functions):
do print "a"
print "b"
-- obviously not the same as
do print "b"
print "a"
and also that it can't eliminate common expressions:
do a <- readBytes file 100
b <- readBytes file 100
doStuff a b
-- obviously not the same as
do x <- readBytes file 100
doStuff x x
it's a way of enforcing ordering in a lazy language where evaluation order isn't really defined.
[handwavy analogy alert]
people often call it "side-effects" as a shorthand. in reality an expression being of type `IO Foo` mostly just tells the compiler that order of execution matters (which isn't the case with pure functions):
and also that it can't eliminate common expressions: it's a way of enforcing ordering in a lazy language where evaluation order isn't really defined.