It is an absence of side effects because side effects are defined as effects that cannot be accounted for. As soon as you have a mechanism to document those effects, they are not "side effects" any more.
hello :: IO ()
hello = putStrLn "Hello World"
`hello` is a "pure" value of type IO (). putStrLn a "pure" function of type -> IO ()
IO () is a value like any other "pure" value, like [Int].
No, that statement evaluates to a pure value of type int. In the process, it does something else as well that effects the visible behavior of the program.
The C equivalent of
putStrLn "hello"
is probably actually
void putsHello(void) {
puts("hello");
}
Unfortunately, function definitions aren't something you can really manipulate at runtime in C.
hello :: IO () hello = putStrLn "Hello World"
`hello` is a "pure" value of type IO (). putStrLn a "pure" function of type -> IO ()
IO () is a value like any other "pure" value, like [Int].