The IO Monad

It is time to study the most important and most intuitive Monad: the IO Monad. IO stands for Input/Output. IO is considered a side effect and it is treated as such. This is why Haskell has a Monad especially for it. In this blog post, we are going to study this Monad. Its study will allows us to better understand the monadic concept in general.

Let us begin with the following program:

module Main where

myFunction   :: String -> Int
myFunction xs = (length xs) + 10 

main :: IO ()
main  =
   do
      putStrLn "Program begins."
      
      print (myFunction "Dimitrios Kalemis")
      
      putStrLn "Program ends."

The output of the following program is:

Program begins.
27
Program ends.

Let us concentate on myFunction. myFucntion takes a String as input and returns an Int as output. This Int is the length of the String plus 10. I do not know whether myFunction is a function worth having, but let us pretend it is. So, we can call myFunction passing any String to it and it will return an Int equal to the number of characters in the string plus 10.

myFunction is purely functional. It takes a String as input, does computations, has no side effects, and produces an Int as output.

Let us now change myFunction from purely functional to monadic. Let the definition of myFunction now be String -> IO Int. The new program with the new myFunction will be as follows:

module Main where

myFunction   :: String -> IO Int
myFunction xs =
   do
      return ((length xs) + 10) 

main :: IO ()
main  =
   do
      putStrLn "Program begins."
      
      a <- myFunction "Dimitrios Kalemis"
      print a

      let b = myFunction "Dimitrios Kalemis"
      b >>= print
      
      myFunction "Dimitrios Kalemis" >>= print
     
      putStrLn "Program ends."

The output of the previous program is:

Program begins.
27
27
27
Program ends.

In the previous program, we can see that the myFunction is monadic. I also showcase the three equivalent ways in which to call the monadic myFunction and print its result.

Although myFunction is expressed in a monadic way (the Monad being IO in this case), myFunction exhibits no side effects. So, let us add IO side effects to myFunction.

The following program showcases a myFunction full of IO side effects: When myFunction is called, and without us having to print or otherwise use its output, myFunction prints to the screen, prompts the user for input, accepts input from the user, concatenates it, and prints it to the screen.

module Main where

myFunction   :: String -> IO Int
myFunction xs =
   do
      putStrLn "Inside the function that returns the length of a string plus 10."
      putStrLn "Type something and press Enter"
      inputData <- getLine
      putStrLn ("This is what you typed: " ++ inputData)
      return ((length xs) + 10)

main :: IO ()
main  =
   do
      putStrLn "Program begins."

      a <- myFunction "Dimitrios Kalemis"

      putStrLn "Program ends."

The output of the previous program follows:

Program begins.
Inside the function that returns the length of a string plus 10.
Type something and press Enter
something
This is what you typed: something
Program ends.

We can see that I typed the word “something” when the program prompted me. All that was written in the screen when myFunction was called was IO side effects.  Why? Because a pure function only accepts arguments as input and returns a result. It cannot do anything else. So, printing to the screen and accepting input from the user are side effects. We welcome these side effects because we need them. The point is that they are “hidden” inside a monadic function. This way, we control and regulate these side effects. And we do not let the side effects surprise us. The function declaration (String -> IO Int) warns us that we will have IO side effects.

The last program finished without really using the Int result from myFunction. But we could have inserted a print a statement after the call of myFunction, in order to see the output of myFunction. Be sure that this output will be the number 27.

As far as the function named “main” is concerned, it has the following declaration: main :: IO (). This declaration means that main is IO monadic (i.e. it may produce IO side effects) and that it takes no input (there is no “<type> ->”) and produces no output, which is denoted by: ().

Finally, since we are talking about a Monad, I would like to show its “return” and “bind” at work. Well, the second program shows “return” in action: “return” in this case takes an Int and returns an IO Int. “return” took its name because it elevates a type a to the type IO a, but it does not produce any IO side effects. Thus, it does not print anything on screen; it just “returns” the IO type. So, “return” “returns” without printing anything to the line.

As far as “bind” (>>=) is concerned, the following program shows this at work. We have three IO monadic functions. Each one has a side effect, which is the putStrLn statement. I show how these are combined in the main program using >>=.

module Main where

myFunction1   :: String -> IO Int
myFunction1 xs =
   do
      putStrLn "Function1."
      return ((length xs) + 10)

myFunction2  :: Int -> IO [Int]
myFunction2 x =
   do
      putStrLn "Function2."
      return ([1..x])

myFunction3   :: [Int] -> IO (Int,Int)
myFunction3 xs =
   do
      putStrLn "Function3."
      return ((length xs, sum xs))

main :: IO ()
main  =
   do
      putStrLn "Program begins."

      myFunction1 "Dimitrios Kalemis" >>= myFunction2 >>= myFunction3 >>= print

      putStrLn "Program ends."

The output of the previous program follows:

Program begins.
Function1.
Function2.
Function3.
(27,378)
Program ends.

myFunction1 takes a string of length 17 and returns the Int 27. 27 is passed to myFunction2 which returns the list [1..27]. The list [1..27] is passed to myFunction3 which returns the tuple (27, 378). The tuple’s first member is the length of the list (which is also the output of myFunction1) and the tuple’s last member is the sum of the numbers from 1 to 27.

To show that returns does not “harm” such compositions, please note that the line in the main program can be equivalently written as:

myFunction1 "Dimitrios Kalemis" >>= return >>= myFunction2 >>= return >>= myFunction3 >>= return >>= print

The previous program showed the composition of three functions that are not purely functional. This is why we used the >>= operator.

As a bonus, I will present the same program, recreating the functions so that they have no side effects, and show the normal way to compose them: with the . operator.

module Main where

myFunction1   :: String -> Int
myFunction1 xs = (length xs) + 10 

myFunction2  :: Int -> [Int]
myFunction2 x = [1..x]

myFunction3   :: [Int] -> (Int,Int)
myFunction3 xs = (length xs, sum xs)

main :: IO ()
main  =
   do
      putStrLn "Program begins."
      
      print ((myFunction3 . myFunction2 . myFunction1) "Dimitrios Kalemis")       

      putStrLn "Program ends."

The output of the previous program is

Program begins.
(27,378)
Program ends.

If you would like to refresh your memory about the normal composition of pure functions, you could (re)read my blog post “Notations for function application and composition in Haskell”.

Advertisements

About Dimitrios Kalemis

I am a systems engineer specializing in Microsoft products and technologies. I am also an author. Please visit my blog to see the blog posts I have written, the books I have written and the applications I have created. I definitely recommend my blog posts under the category "Management", all my books and all my applications. I believe that you will find them interesting and useful. I am in the process of writing more blog posts and books, so please visit my blog from time to time to see what I come up with next. I am also active on other sites; links to those you can find in the "About me" page of my blog.
This entry was posted in Development. Bookmark the permalink.