On the Stack Overflow site, I found a great question titled Monads vs. Arrows. Essentially the OP (original poster) asks: “… When should I use monads and when should I use arrows?”.
I answered the question and since I believe that I raise some important points in my answer, I decided to duplicate my answer here as well. My answer follows:
The short answer is that Arrows are more general than Monads and are also more cumbersome to use. Thus you should use Monads whenever you can, leaving the use of Arrows for the cases where Monads are not applicable.
The “scenic route” answer follows.
John Hughes, the person who introduced Arrows, has published two great papers that I recommend: “Generalising monads to arrows” and “Programming with Arrows”. These two articles are easy to read and provide the answer to your question. Even if some people do not understand all the details or the code in these two articles, they will certainly find lots of information and very helpful explanations about Monads and Arrows.
I will now highlight the main points from these articles that pertain to your question.
When Monads were introduced, people thought that they were all-powerful. Indeed, Monads pack a lot of power. But at some point, it was found that there are cases where Monads cannot be applied. These cases have to do with multiple inputs, especially when some of the inputs are static and some of the inputs are dynamic. So, John Hughes stepped up and introduced Arrows.
Arrows are more general than Monads. Arrows are a superset of Monads. They can do all that Monads do and more. But they are also more difficult to use. John Hughes recommends that you should use Monads whenever you can and that you should use Arrows when you cannot use Monads.
I agree with John Hughes. I am also reminded of Einstein’s quote “Everything should be made as simple as possible, but not simpler”.
Of course, it all depends on the particular situation at hand. Let me explain. Let us suppose that you are learning Haskell. Then it would be a great task to do each program using a monadic approach and redo it using an arrow-based approach. When you learn, you should strive to explore all possibilities and implement all kinds of approaches. This way, you obtain great insight and you are able to compare different solutions first-hand.
Now let us suppose that you want to provide a library to the community. Well, you owe it to the people that will read your code that you should use the approach that is easiest to understand and still gets the job done. You also owe it to the people that will use your code that your solution lacks unnecessary complexity. This way, your solution is more easily maintainable and less prone to errors and bugs.
But what if you are in a borderline case? Let us suppose that you are not sure whether or not you will need the extra power of Arrows. Then what should you do? Should you start with a monadic approach and later switch to an arrow-based one if the need arises? Or should you start with Arrows from the get-go, avoiding a costly switch halfway through the project?
Again, my answer is to try the first approach: try to use Monads if you can. If you later find out that you cannot use Monads, you will have to endure a costly switch, where you will have to restart and redo the project in order to use Arrows. This approach will certainly require more time and other resources from your part. But you will know that you did the correct thing, which was to try to provide the simplest, clearest, less complex solution possible.
Avoiding unnecessary complexity is the most important thing. Believe it or not, this is the reason concepts (such as function composition, Monads and Arrows) from Category Theory were introduced to Computer Science. Ironic?