I would say `unsafePerformIO` exist for calling C code. Since the Haskell type system can't prove that the C function you're calling is pure, the programmer needs to assert this fact by using `unsafePerformIO` at the call site. There are other possible uses of course, but I'd say this is the main one.