The I/O system in Haskell is purely functional, yet has all of the expressive power found in conventional programming languages. To achieve this, Haskell uses a monad to integrate I/O operations into a purely functional context.
The I/O monad used by Haskell mediates between the values natural to a functional language and the actions which characterize I/O operations and imperative programming in general. The order of evaluation of expressions in Haskell is constrained only by data dependencies; an implementation has a great deal of freedom in choosing this order. Actions, however, must be ordered in a well-defined manner for program execution -- and I/O in particular -- to be meaningful. Haskell's I/O monad provides the user with a way to specify the sequential chaining of actions, and an implementation is obliged to preserve this order.
The term monad comes from a branch of mathematics known as category theory. From the perspective of a Haskell programmer, however, it is best to think of a monad as an abstract datatype. In the case of the I/O monad, the abstract values are the actions mentioned above. Some operations are primitive actions, corresponding to conventional I/O operations. Special operations (methods in the class Monad, see Section 6.2.5) sequentially compose actions, corresponding to sequencing operators (such as the semi-colon) in imperative languages. Finally, the hidden implementation can be thought of as the system state; i.e. the state of the world.
Output Functions
These functions write to the standard output device (this is normally
the user's terminal).
For example, a program to print the first 20 integers and their
powers of 2 could be written as:
Input Functions
These functions read input from the standard input device (normally
the user's terminal).
By default, these input functions echo to standard output. Functions
in the I/O library provide full control over echoing. The following program simply removes all non-ASCII characters from its
standard input and echoes the result on its standard output. (The
isAscii function is defined in a library.)
Files
These functions operate on files of characters. Files are named by
strings using some implementation-specific method to resolve strings as
file names.
The writeFile and appendFile functions write or append the string,
their second argument, to the file, their first argument.
The readFile function reads a file and
returns the contents of the file as a string. The file is read
lazily, on demand, as with getContents.
The do notation allows programming in a more imperative syntactic
style. A slightly more elaborate version of the previous example
would be:
The return function is used to define the result of an I/O
operation. For example, getLine is defined in terms of getChar,
using return to define the result the monad:
7.1 Standard I/O Functions
Although Haskell provides fairly sophisticated I/O facilities, as
defined in the IO library, it is possible to write many
Haskell programs using only the few simple functions which are
exported from the Prelude, and which are described in this section.
putChar :: Char -> IO ()
putStr :: String -> IO ()
putStrLn :: String -> IO () -- adds a newline
print :: Show a => a -> IO ()
The print function outputs a value of any printable type to the
standard output device (this is normally the user's terminal).
Printable types are those which are instances of class Show; print
converts values to strings for output using the show operation and
adds a newline.
main = print ([(n, 2^n) | n <- [0..19]])
getChar :: IO Char
getLine :: IO String
getContents :: IO String
interact :: (String -> String) -> IO ()
readIO :: Read a => String -> IO a
readLine :: Read a => IO a
Both getChar and getLine raise an exception on end-of-file; the
IOError value associated with end-of-file is defined in a library.
The getContents operation returns all user input as a single
string which is read lazily as it is needed. The interact
function takes a function of type
String->String as its argument. The entire input from the standard
input device (normally the user's terminal) is passed to this function
as its argument, and the resulting string is output on the
standard output device. The readIO function is similar to read
except that it signals parse failure to the I/O monad instead of
terminating the program. The readLine function combines getLine and
readIO.
main = interact (filter isAscii)
type FilePath = String
writeFile :: FilePath -> String -> IO ()
appendFile :: FilePath -> String -> IO ()
readFile :: FilePath -> IO String
Note that writeFile and appendFile write a literal string
to a file. To write a value of any printable type, as with print, use the
show function to convert the value to a string first.
main = appendFile "squares" (show [(x,x*x) | x <- [0,0.1..2]])
7.2 Sequencing I/O Operations
The two monadic binding functions, methods in the Monad class, are
used to compose a series of I/O
operations. The >>
function is used where the result of the first operation is
uninteresting, for example when it is (). The >>= operation
passes the result of the first operation as an argument to the second
operation.
(>>=) :: IO a -> (a -> IO b) -> IO b
(>>) :: IO a -> IO b -> IO b
For example,
main = readFile "input-file" >>= \ s ->
writeFile "output-file" (filter isAscii s) >>
putStr "Filtering successful\n"
is similar to the previous example using interact, but takes its input
from "input-file" and writes its output to "output-file". A message
is printed on the standard output before the program completes.
main = do
putStr "Input file: "
ifile <- getLine
putStr "Output file: "
ofile <- getLine
s <- readFile ifile
writeFile ofile (filter isAscii s)
putStr "Filtering successful\n"
getLine :: IO String
getLine = do c <- getChar
if c == '\n' then return ""
else do s <- getLine
return (c:s)