Output
With Haskell's do
notation, we can read input, write output, generate random numbers, and perform any other activity that produces new state. To make a standalone program that has the side effect of printing output, we define main
as a do
block of print statements:
main = do
putStr "Goodbye"
putChar ','
putChar ' '
putStrLn "Pluto!"
Each of the put*
functions returns an altered environment that gets passed along to the next function in the sequence. But we don't see any of this alteration happening. The do
syntax makes it implicit. This state-passing mechanism is implemented via an abstraction called a monad, which we will not discuss in further detail.
Put this source code in a file named main.hs
and then run it with this command in the shell:
runhaskell main.hs
This command compiles the script on the fly and then runs the executable. If we wish to build a standalone executable, then we must explicitly compile the program with ghc
:
ghc main.hs
./main
Consider the type signatures of the three common output functions putChar
, putStr
, and putStrLn
, as reported by ghci
:
> :t putChar
putChar :: Char -> IO ()
> :t putStr
putStr :: String -> IO ()
> :t putStrLn
putStrLn :: String -> IO ()
Some parts of these signatures should seem familiar. The arrows mean that these are functions. The parameter types show that the functions must be passed a Char
or String
. But then there's the mysterious return type: IO ()
, which we'll learn more about soon.
There is no putInt
or putDouble
or putList
. If we want to print something that is not a Char
or String
, we must first turn it into text. The polymorphic show
function turns a value into a String
:
main = do
putStrLn (show [1..10])
But check out the type signature of function print
:
> :t show
print :: Show a => a -> IO ()
The print
function accepts a parameter of any type that implements the Show
typeclass. It calls show
for us and prints the result on its own line. This call is equivalent to but much simpler than the earlier putStrLn
:
main = do
print [1..10]