Are monads (I mean more complex than just Option) useful if my program is mostly without side effects? I get how the IO Monad is great, but what I don't really need it?
Yes! I think side-effects are just one little use-case for monads. They can be useful any time you want to compose functions together but you want to do something else with the composition and data. As Bartosz Milewski so wonderfully explained here [1], Monads are just "a way of composing special types of functions" or "function composition with some kind of flair!"
I was recently writing a Unger-parser-like natural language parser and I wanted to compose functions that took the parse results from one parse function and then parse something else. I noticed that every time I wanted to pass the results to another function I had to do something with the leftover tokens and also coallate the error messages that might have been building up. What if I used monads to automatically handle the resulting tokens and error messages each time I reached for another parse function? I wrote a monadic binding function that handled the tokens and error messages for each parse function, and then I was able to happily compose my parsing functions in a much more clean and concise way.
Monads are great anytime you want to do something more along with your function composition other than just passing one piece of data directly from one function to another.
"And then you say... What if I used a different kind of composition? Also function composition, but with some kind of flair, something additional. If I redefine composition, I have this one additional degree of freedom in defining composition. If I use this additional degree of freedom, I am using a monad." - Bartosz Milewski [1]
You can use monads to track where effects are used, and scope them. This allows you to write pure functions that uses effects in their implementation, but the users of the function need not worry about that.
Some monads are also handy for structuring certain kind of code---for example, the non-determinism monad can be handy for writing searches, etc.
Yes! Consider jq. jq is almost a 100% pure functional programming language [0], and it's a lot like using the list monad in Haskell.
Various monads basically help you manage things like configuration parameters that affect your computations. Your code can all be deterministic, and yet using a monad can help you write cleaner code.
Just because the IO monad is used for impure actions doesn't mean that monads in general are used for impure actions.
[0] There are a few builtins that have side-effects: `input` and `inputs` (which consume inputs), and then there's `stderr` and `debug` (which emit output to `stderr`). Other than this, every jq program represents a pure transformation of its input.