fixIO allows recursive IO operations to be defined.
The first argument to fixIO should be a function
that takes its own output as an argument (sometimes called "tying the
knot").

unsafePerformIO :: IO a -> a

This is the "back door" into the IO monad, allowing
IO computation to be performed at any time. For
this to be safe, the IO computation should be
free of side effects and independent of its environment.

If the I/O computation wrapped in unsafePerformIO
performs side effects, then the relative order in which those side
effects take place (relative to the main I/O trunk, or other calls to
unsafePerformIO) is indeterminate.

However, it is less well known that
unsafePerformIO is not type safe. For example:

This program will core dump. This problem with polymorphic references is
well known in the ML community, and does not arise with normal monadic use
of references. There is no easy way to make it impossible once you use
unsafePerformIO. Indeed, it is possible to write
coerce :: a -> b with the help of unsafePerformIO.
So be careful!

unsafeInterleaveIO :: IO a -> IO a

unsafeInterleaveIO allows IO
computation to be deferred lazily. When passed a value of type
IO a, the IO will only be
performed when the value of the a is demanded.
This is used to implement lazy file reading, see
IO.hGetContents.

GHC's implementation of the IO library distinguishes between
binary- and text-mode files. This unfortunate hack is imposed on us
by the need to support Win32 platforms.

On Win32, files opened in text mode are subject to CR-LF translation.
When reading a handle in text mode, CR-LF sequences in the physical
file are translated into lone LFs in the stream presented to the
Haskell program. Writes to a text mode handle are subject to the
inverse transformation.

On Unix platforms there is no such translation. What you get is
exactly the contents of the file, and vice versa.

Unfortunately this behaviour makes it difficult to correctly implement
file-positioning operations in text mode on Win32. If you want to use such
operations, you must first place the handle in binary mode. Failure
to do so results in IO exceptions being raised. This applies only to
Win32, and not to any other platforms. If your programs use seek
operations and you want them to be portable between Unix and Win32,
you need to ensure the relevant handles are in binary mode.

You can get hold of a binary-mode file handle one of two ways. Either
open the file with openFileEx, which allows the
mode to be specified. Or, if you already have an open handle,
use hSetBinaryMode to change its mode.

Also as a result of this, note that on Win32 there are also several
operations which, whist still allowed, may give different results in
text mode than their Unix counterparts. These are: changing buffering
modes of a handle (hSetBuffering), and writing to a
read-write handle. In both cases, the read-buffer associated with the
handle needs to be flushed, and, due to the Win32 text mode
translation, the resulting physical file position following the flush
may be wrong.

This issue of seeking in the presence of a non-identity transform
between file and buffer contents will need to be revisited when the
library is re-done to properly support Unicode. The present
arrangement is the least-worst kludge we could come up with at present.

When called, trace prints the string in its first
argument to standard error, before returning the second argument as
its result. The trace function is not
referentially transparent, and should only be used for debugging, or
for monitoring execution. Some implementations of
trace may decorate the string that's output to
indicate that you're tracing.

The IO module provides several predicates
over the IOError type, such as
isEOFError,
isDoesNotExistError, and so on. Here we define
an extended set of these predicates, taking into account more
types of error:

unsafePtrEq compares two values for pointer equality without
evaluating them. The results are not referentially transparent and
may vary significantly from one compiler to another or in the face of
semantics-preserving program changes. However, pointer equality is useful
in creating a number of referentially transparent constructs such as this
simplified memoisation function: