Latest revision as of 21:57, 8 May 2010

The following is inspired by Luke Palmer's post. This only describes one possible semantics of

IO a

; your actually implementation may vary.
The idea is to define

IO

as

dataIO a = Done a
| PutChar Char(IO a)| GetChar (Char->IO a)

For simplicity this an example of

IO

that only gives semantics for teletype IO.
Think of

IO a

as a tree whose leaves are

Done a

that holds the result of the program.

PutChar

is a node that has one child tree and the node holds one character of data.

GetChar

is a node that has many children; it has one child for every

Char

, but

GetChar

holds no data itself.

This tree contains all the information needed to execute teletype interactions.

One interprets (or executes) an

IO a

by tracing a route from root of the tree to a leaf.
If a

PutChar

node is encountered, the character data contained at that node is output to the terminal and then its subtree is executed. It is at this point that Haskell code evaluated in order to determine what character should be displayed before continuing. If a

GetChar

node is encountered, a character is read from the terminal (blocking if necessary) and the subtree corresponding to the character received is executed. If

Done

is encountered the program ends.

Done

holds the result of the computation, but in the case of

main ::IO()

the data is of type

()

and thus contains no information and is ignored.

This execution is not done anywhere in a haskell program, rather it is done by the run-time system.