Each of your points contain mistakes:
Exceptions in Haskell are separate from successful execution paths. It has `throw :: Exception e => e -> a`, which can be used in pure code. Only catching, i.e. handling exceptions, has to be in IO.
The IO issue you mention is a feature to me: it means your code will have a very clean structure with IO at the top level, and pure domain code further down. But perhaps others prefer other structures, I don't know.
Field names are not global. They are scoped the same way as everything else. They are in scope in the module, and you can manage visibility in other modules with exports and imports.