The struggle I'm having isn't that I can't do I/O; it's that I can! ;P
Seriously, though, it's that I can do so in places that I'm pretty sure that I shouldn't. That is, my understanding is that things that are dirty, like anything involving I/O, should be sectioned off from everything else that is pure, as much as possible. This makes more of your code understandable, testable, and reusable.
Indeed, I have striven to do that but there are places where I have taken the easy way out in the middle of routines that "update" state. Below is an example from my game (you can look at the entire source here, https://github.com/quephird/space-invaders/blob/master/src/space_invaders/core.clj):
(defn key-pressed [{{sound :sound} :player-bullets :as state}
{key :key
key-code :key-code :as event}]
"Primary hook to return new version of game state taking into account: