Clojure's immutable types mean just that—the values are immutable (e.g., in CL you could do (defvar x 42) (setf x 84), but in Clojure the equivalent is not allowed).
But there are many more side effects than changing variables. Clojure makes no guarantees that a given function is referentially transparent, nor does it promise that a given function doesn't write to stdout/stderr, read from stdin, change the file system, make a network connection, send missiles to foreign countries, etcetera.
Not arguing that the following method can be trusted as much as in Haskell but properly used also prevents this kind of scenario:
https://clojuredocs.org/clojure.core/io℅21
The 'io!' macro marks a code block to have side effects which prevents using it in a STM transaction as 'dosync' might need to execute the functions multiple times in case of conflicts during the optimistic locking.
Dont Clojure's immutable types handle the side-effect issues well? Without the contortions that Haskell puts you through.