> I think a Monad is an abstraction which generalizes a transition, similar to a step of logical deduction.

I think this is where we're going off the rails. A monad is a specialisation of a functor (the infamous "A monad is monoid in the class of endofunctors", which I'll explain as briefly as I can later).

A functor is simply a mapping from one set to another (possibly the same set). So if I have a function that maps integers to characters, for instance this whole setup (the set of integers, the set of characters and the function that maps them from one to the other) is a functor. It gets a bit complicated when we translate that to a programming language. The set of all integers is a type. The set of all characters is a type. So we can describe the mapping from one to the other as a functor. You will probably just recognise that as a function -- and, indeed, a function is a specialisation of a functor (although the general case of a function is quite complex and I don't have anywhere enough space to explain it).

What is important to understand, though, is that the thing we are trying to map is important. We need somewhere to hold that value so that we can map it from one type to another type. Any container will do (a list, array, tuple, or even a partially applied function). When we are talking about programming with functors, we implement them using that container and a function that transforms the content from one type to another (possibly the same type).

When we map the value from one type to another, we want to put the value in a functor as well (going back to the original concept of mapping from one set to another, after we've mapped it, it is still a functor -- just one potentially holding a different typed value). Normally we implement a function called "map" (or "fmap", for "functor map") on a functor that accepts a transforming function that maps a value from one type to another. "map" then puts the result back into a functor (which is just the same kind of container you started with). As I can see you are familiar with FP languages, I'm sure you are familiar with this implementation. You have an array of integers, you can call "map" with a function that maps integers to characters and the result is an array of characters. Both the array of integers and the array of characters are functors.

Again, I'll reiterate this because it will be super important later: any container is a functor if you can map it's contents. In fact, anything that can hold state (like a partially applied function) is a functor if you can map its contents.

A monad is a specialisation of a functor. Let's go back to "A monad is a monoid in the category of endofunctors". I like that quote because it sounds like nonsense, but it's really very easy to understand if you know the definitions of the words.

A "monoid" is another specialisation of a functor. It's a functor for which there is a binary operator and an identity element. For example, let's take integers. They are monoids under addition. If we take 2 integers and add them together. There is a particular integer, "0" for which when you add anything to "0" you get the same result again. We can use addition in our mapping function (usually one of the parameters being partially applied) and if we pass it "0" we'll get the same value back.

Monoids seem a bit pointless, but they make a lot more sense when you are thinking about folding. A container isn't foldable unless is is a monoid (it's a great kata to do, if you are interested). There are lots of other implications, so I highly recommend implementing the whole hierarchy of functors from scratch to see why things are as they are.

Getting on to monads, we now know what a monoid is (not too difficult to understand). We only need to know what an "endofuctor" is. This one is also really easy to understand. An endofunctor is a functor for which the result of "map" results in exactly the same type as the original. So if you had an array of integers to start with, you end up with an array of integers. That's all it is.

"A monad is a monoid in the category of endofunctors". The last bit we have to explain is what a category is. It's just a grouping of mappings. I could have a mapping that adds 5 to an integer. I could have a mapping that adds 6 I could have one that subtracts 1. Each one of those mappings gives a result in the set of integers. As a grouping of mappings (i.e. "category"), all of these mappings describe an "endofunctor".

Finally we get to the result. In very plain terms, a monad is just a container (or anything that can hold state) which has a function that modifies the contents such that the result is guaranteed to have exactly the same type. That's all it is.

It sounds disappointingly plain, but it has surprising power. For one thing a monad holds state. Obviously this is important. But if you use the monadic mapping function (usually called "bind"), then you are guaranteed to be returned a monad of the same type. This allows you to freely chain operations without fear that something will break. This is what a monad is for.

Where I think you are getting confused is that you have seen advanced applications of monads and you have come under the mistaken impression that these are the reasons monads exist. Indeed, monads are required in a pure functional language to do those things. The nice thing about a monad is that it can hold any state for which there is an identity element. The implementation of the container is completely beside the point. As I said, you can partially apply a function and that is potentially a monad.

The even more interesting thing is that any abstract datatype can also be a monad. Where you are saying, "We don't need monads for this, we only need abstract data types", it's a bit nonsensical. These ADTs will be monads if they are used in that fashion. Indeed, objects in OOP are often monads! They just need a transforming function that returns the same type of object.

I hope you found that interesting/helpful.