To get acquainted with unsafePerformIO (how to use it and when to use it), I\'ve implemented a module for generating unique values.
Here\'s what I have:
The purpose of unsafePerformIO is when your function does some action internally, but has no side effects that an observer would notice. For example, a function that take a vector, copies it, quicksorts the copy in-place, then returns the copy. (see comments) Each of these operations has side effects, and so is in IO, but the overall result does not.
newUnique must be an IO action because it generates something different every time. This is basically the definition of IO, it means a verb, as opposed to functions which are adjectives. A function will always return the same result for the same arguments. This is called referential transparency.
For valid uses of unsafePerformIO, see this question.