Reading from stdin and Writing to stdout
putChar, putStr, and putStrLn
The first three functions you want to use to print stuff on screen are
putChar :: Char -> IO ()
putStr :: String -> IO ()
putStrLn :: String -> IO ()
Neither of them returns anything. You call them only for their effects, which is
to make characters appear on your screen. putChar
prints a single character.
putStr
prints a whole string. putStrLn
does the same as putStr
but adds a
newline character at the end to start a new line:
>>> putChar '0'
0>>> putStr "Hello"
Hello>>> putStrLn "Hello"
Hello
The output of the first two actions may not be obvious. They appear before the
prompt of the next line, exactly because neither putChar
nor putStr
adds a
newline at the end.
There's not much more to be said about these functions. The one thing to point
out is that C programmers are used to using printf
to format all kinds of
data other than string data:1
int x = 5;
double y = 3.14;
printf("x = %d, y = %lf\n", x, y);
To do the same, Java programmers use the fact that, for example, adding a number to a string automatically converts the number to its string representation.
int x = 5;
double y = 3.14;
System.out.println("x = " + x + ", y = " + y);
Haskell performs none of these automatic conversions. However, we have the good
old show
function that allows us to convert any value of a type that is an
instance of Show
into its string representation. Thus,
>>> x = 5
>>> y = 3.14
>>> putStrLn $ "x = " ++ show x ++ ", y = " ++ show y
x = 5, y = 3.14
The function that GHCi uses to print the values of expressions entered at the prompt is
print :: Show a => a -> IO ()
print = putStrLn . show
print x
converts x
to its string representation, displays this string
representation on screen, and adds a newline at the end, because it uses
putStrLn
, not putStr
, to print the string representation of x
:
>>> print x
5
>>> print "Hello"
"Hello"
>>> print Nothing
Nothing
As with any program, printing "on the screen" means printing to stdout
, the
program's standard output stream. Normally, this is the screen, but for
example, if we pipe the output of our program to some other program, then our
program's stdout
becomes the stdin
of the other program, and nothing will be
printed on screen. Instead, the other program reads the output our program
produces and hopefully does something useful with it.
getChar, getLine, and getContents
Simple interactive programs can be built by reading individual characters or
entire strings from the keyboard. Only, they aren't really read from the
keyboard but from stdin
, our program's standard input stream. This input may
be provided by the keyboard or by the stdout
stream of another program if we
pipe that program's output to our program's input.
We read a single character using
getChar :: IO Char
Note that this is an IO
action that takes no arguments. It has type IO Char
.
In C, this function would have the type getChar :: Char
. But in Haskell, a
value of type Char
is just a value, or at best a zero-argument function that
always returns the same result of type Char
. The type IO Char
means that
getChar
may return a different result every time we run it. It's an action
that returns a value of type Char
. That's because IO Char
is a wrapper
around the function type RealWorld -> (Char, RealWorld)
. The result returned
by getChar
depends on the state of the world, and the part of the state of the
world that getChar
depends on is the last character that was added to our
program's stdin
.
Here's getChar
in action in GHCi:
>>> getChar
1'1'
The weird-looking output was produced as follows: I pressed 1 on my
keyboard. That echoed the character 1
on the screen. getChar
read that 1
.
Since I was inside GHCi, GHCi printed the result returned by getChar
, which
was '1'
. Just as in C, there exist more fine-grained facilities that allow us
to read characters without echoing them to the screen, but they are beyond the
scope of this book.
For reading an entire line, we have
getLine :: IO String
>>> getLine
This is a line.
"This is a line."
We can also read the entire contents of stdin
using
getContents :: IO String
This doesn't work so well in GHCi, but you can use it in compiled programs to
read all of stdin
. I won't demonstrate the use in GHCi. If you do try, the
only thing you can do to get getContents
to return is is to press Ctrl+C,
and then you get a strange error message. That's a known bug. The problem is
that Ctrl+D, the usual method to signal the end of file when entering them
on the terminal, sends EOT
in GHCi, and getContents
simply reads this
character without interpreting it as the end of the file. I'm not sure who is to
blame here, whether GHCi should actually close stdin
upon seeing EOT
or
whether getContents
should interpret it as end-of-file.
We'll look at getContents
a little later, when talking about the
interaction between Haskell's lazy evaluation and I/O.
-
For all its archaic syntax of the format string, C's
printf
is a tried and tested approach to formatting data. Thus, Haskell programmers felt they couldn't really do without such a powerful tool. TheText.Printf
module provides Haskell's version ofprintf
. With it, we can print things more or less in C style:GHCi>>> import Text.Printf >>> printf "x = %d, y = %f\n" x y x = 5, y = 3.14
I don't use this facility very often. Other Haskell programmers may use it more. ↩