I think to do that sort of thing, it’s easiest to convert to a string, use the string manipulation functions, then convert back to an atom. That’s been my experience, at least…
I was not aware of this predicate and it’s really useful!
Only a minor comment: for Dan’s use case I think the flag is /g instead of /a: re_replace(' '/g, '_', 'abc def ghj', X).
The advantage is that you do not rely on the pcre package. Didn’t test the relative performance. I would expect both these are considerably faster than the route via atom_codes/2. That route is attractive for more complicated rewrites where DCGs start to show there power and for portability.