On this page:

2.4 I/O

type constructor

(IO a)

The type of I/O actions, which are monads. Hackett’s encoding of I/O is semantically pure—evaluating an I/O action does not cause any side-effects to be performed. The only way to “execute” an I/O action is to provide it to the main form, which instructs the Hackett runtime to perform the actual I/O actions described by the IO value.

It may be helpful to think of a value of type (IO a) as a set of instructions to obtain a value of type a. This makes it clear that it is impossible to get the value “inside” an IO action, since no such value exists; there is no String “inside” a value of type (IO String).

Since main is the only way to ask the runtime to execute the instructions contained within an IO action, and main is only legal at the top level of a module, it is impossible for I/O to be performed locally. This is how Hackett preserves referential transparency even within functions that produce values of type IO.


(main action)

  action : (IO a)
Appends an I/O action to the current module’s main actions. Main actions are executed by the runtime whenever a module is run directly, e.g. from DrRacket or via the racket executable, rather than imported via require. This form is only legal at the top level of a module.

Uses of this form correspond to definitions of main submodules in #lang racket. For more information, see Main and Test Submodules.


print : (forall [m] (Monad-Base IO m) => {String -> (m Unit)})

Produces an I/O action that prints the given string to standard output.


println : (forall [m] (Monad-Base IO m) => {String -> (m Unit)})

Like print, but appends a newline to the end of the printed message.