So you have your nicely written function of type int -> int
, but now you want to add some trace statements while it executes, but you can't because it is purely functional. Then someone helpfully suggests monads. Now all you have to do is rewrite it to be of type int -> IO int
, and rewrite all of the calling functions to be of monadic type too, and so on all the way up to the top level of the program...
The fundamental problem is that you need some way to separate the program itself (which is purely functional) from whatever scaffolding and diagnostic code you put in to help test and debug it. In imperative languages you can happily mix the two together but in pure functional programming you can't, and the language environment has to provide explicit support for trace messages and the like.