
Can someone please explain how IO operations do not fit in the pure category of mathematical function in that they have to be implemented via Monads.
Let's not talk about monads at all. Instead allow me to eliminate a major misconception right away: I/O does in fact fit into the model of purity. The first major insight occurs when you stop thinking about getLine as a function or an "impure value". What it really is is just a program. Every value of type `IO A` is in fact a program that produces a value of type `A`. The second major insight happens when you realise that the equal sign in Haskell introduces an actual equation: line = getLine Here `line` is not a string either. You have just expressed that `line` is the exact same thing as `getLine`, so it is a program that, when run, produces a string. Now realise that `main` is also a program, the one that is the actual program you are writing. When putStrLn "Hello!" is a program that prints a string and results in the dummy value `()` and you say that main = putStrLn "Hello!" then the program you are writing is that same program, because you have just defined it to be (remember: equation, not assignment!). Now you have a bunch of programs. The final ingredient to writing side-effecting programs in Haskell is a way to combine those programs. If `p1` and `p2` are two programs, then `p1 >> p2` is the program that first executes `p1` and then executes `p2`. Its result is the result of `p2`, the latter argument of `(>>)`: main = putStrLn "Hello" >> putStrLn "world!" You have just defined `main` (i.e. your program) to be the program that first prints the "Hello" line, then prints the "world!" line. A more powerful operator is `(>>=)`, which is best explained in terms of an example: putStrLn :: String -> IO () Now this is an actual function. It takes a string and returns a program, the one that prints that string. Remember that functions are the things that can be applied. Not everything is a function, and in particular programs are *not* functions, a common and dangerous misconception. You could just pass it a string, but what if you want to print whatever `getLine` resulted in? (>>=) :: IO a -> (a -> IO b) -> IO b Verify that the first argument fits the type of `getLine`, the second one fits the type of `putStrLn`. Here is the specialised version, where `a = String` and `b = ()`: (>>=) :: IO String -> (String -> IO ()) -> IO () Simply apply it to the desired program (`getLine`) and the desired program-producing function (`putStrLn`) to get a composite program: main = getLine >>= putStrLn Now let's talk about monads. It is a nice incident that the `(>>=)` operator follows a nice algebraic structure (monads), just like addition follows a nice algebraic structure (monoids). And it is not the only operator that follows this structure. What we can do now is to write extremely generic functions that work for all monads with a single implementation. Welcome to the world of composable polymorphism. This is the main reason why Haskell code tends to be much shorter than code in other languages. Functions like `mapM` and `forever` are not IO-specific. They work for all monads, yet they have only a single implementation. So yes, you are right. Monads are not at all necessary. But they are useful, because we get so much for free. Basically when you implement a control functions, chances are that you can actually apply it to a wide variety of structures, including `IO`. Exercise: Can you think of an operation that would work for all monoids? If you can, you can implement it *in its general form* in Haskell. In other words, once you have established that some type denotes a monoid, that operation will be available for it for free. Same thing with monads.
For e.g. the getLine function has the type IOString and it reads the input from the user. Now as I see it the output of getLine will always be same if the input remain same (i.e. for input "X" getLine will always return "X" ) which is the constraint on mathematical functions.
Therefore I don't see why monads are necessary for implementing IO in pure languages.
I can understand why Date and Random functions have be implemented via monads because their output will always change.