if the data is truely immutable logically, then unsafePerformIO or FFI importing as a pure function is okay. unsafePerformIO is not inherently bad, it just shifts the burden of proof from the compiler to you as to whether an operation is referentially transparent. Now, this is actually something tricky and non obvious to prove sometimes, so you are introducing an opprotunity for a big bug that is very difficult to track down, NEVER assume that just because you can't think of a bug with pretending something is referentially transparent but isn't means it is okay. The compiler is very very clever about exploiting opprotunities to optimize things. So make sure you are actually proving immutable referential transparency and not just convincing yourself that you can't think of a way it can go wrong. But if your proof is solid, then you should not feel guilty for using it.
Note that this could have consequences when it comes to memory consumption, immutable values can stick around in unevaluated thunks and since the haskell compiler only knows your object through a ForeginPtr it has no idea how expensive it is to retain, so logical immutability may not be enough if your immutable object is large enough that the explicit lifetime aspects of using the IO monad is useful to you. the type system guarentees correctness, efficiency is another matter.