Why would you write a program without interacting with the real world? If there is no observable behavior of your program, than it might not even exist. The IO type is responsible in Haskell to describe a program's interaction with the outside world.
Let's have a look at the type signature of a few functions that interact with the outside world.
getLine :: IO String
The getLine function reads a line from the command line. Let's have a look at the type of the return value. We have String in there which stands for the line that is read from the command line. IO is a type that expects one type parameter, in this case it is String. We have seen types with type parameters already. IO is just one of them.
IO a describes an action that will eventually result in a value of type a. getLine is an action that when it gets executed will result in a value of type String. Think about these actions as recipes. It is a list of things to do. When your program is executed they are going to be cooked. Until then they are just arbitrary values with a type of IO a.
putStrLn :: String -> IO ()
The putStrLn function prints its first parameter to the screen. The return value is much more interesting. () is called unit. Unit has one possible value ().
It would look like this as an ADT:
data () = ()
Let's get back to the original type IO (). putStrLn describes an IO action that when executed will result in a value of type (). In this case () is just a placeholder, since IO has to have a type parameter, but an action that prints to the screen has nothing meaningful to return with.
Haskell is a pure language. This means that if I call a function with the same parameters it will return the same value every time. Languages like python are not pure. A python function might set a global random seed. Functions depending on this random seed generate an unexpected value every time the seed is modified. The functions modifying the random seed and generator have side effects, in this case they mutate global state. If functions are allowed to have side effects, the language can not be pure.
We have just seen two functions that interact with the user. Printing to the screen changes the state of the world. Isn't that global mutable state? If I read a line from the console, it returns a different line every time I execute it. How is Haskell pure than?
The answer to all of this questions lies in the fact that the functions interacting with the world return a value of type IO a, not a. They just describe the action that is going to happen when they are executed. However the action is the same every time.
putStrLn "hello" will return an IO action that prints out "hello" to the screen every time it is called. The IO action is the same as long as the parameters are the same.
Create a file Greet.hs with the following content:
module Main where
readName :: IO String
readName = do
putStrLn "What is your name?"
getLine
main :: IO ()
main = do
name <- readName
putStrLn ("It is nice to meet you " ++ name ++ "!")
Compile it with ghc and start it.
$ ghc ./Greet.hs
$ ./Greet
There are a lot of interesting things here that we will explain only in the next lecture. Try to use the patterns in the example to complete the exercises.
- Create the type Person that has the following fields: name, age. (age can be String for now)
- Ask the user to enter a name and an age.
- Build a value of type Person with the values entered by the user.
- Print out the value you have created.