I think Monad make more sense when you understand the context and its additional constraints as to why you'd want to use Monads.

Imagine that you want to write code that is strictly pure, so that when evaluated, it doesn't do any side effect, it only returns a pure description of the computation it did.

Here's a simple example:

func announce-good-catch(fish)
if(fish.size > 5)
print "We found a big one!
else
print "That one won't even feed a cat, try again!"

How can you make this function pure? It technically prints something, yet you want to run it and not have it print, but instead return you something that describes what branch it took and what effect it would have done had it been impure.

You can do this:

func announce-good-catch(fish)
if(fish.size > 5)
return [:print "We found a big one!]
else
return [:print "That one won't even feed a cat, try again!"]

Now the function is pure, it doesn't print anything, it evaluates the branch to take based on input and return to you a description of what it should do.

The problem is programming that way is hard, let's add another function:

func get-fish(fish-id)
return new Fish(db.query("select * from fish where id = " + fish-id)

Again this isn't pure, so let's make it pure:

func get-fish(fish-id)
return [:new Fish [:query [:get :db] ["select * from fish where id = " + fish-id]]])

Already this is getting pretty complicated, getting the DB is impure so return a description of it, querying the DB is impure, so return a description of it, Fish can't be created if we don't execute the side-effect, so take note that you want to create a Fish after those two side-effects were to actually happen.

Now combine get-fish and announce-good-catch(fish)?

announce-good-catch((get-fish 10))

Well this works in the impure version, but this doesn't work for our pure versions because now the input to announce-good-catch isn't an actual Fish but instead it's this:

[:new Fish
[:query
[:get :db]
["select * from fish where id = 10"]]]

And so it's not going to work, you can't compose those pure functions as you'd normally would.

Here is where Monads become really handy, they put all this behind an abstraction that lets you with added syntax sugar feel like your code is written in the impure style, yet under the hood it will convert things to a description of all side-effects and whatever pure behavior is pending the result of those. And it'll let you compose those descriptions together so that from the programmer point of view the code you write looks and feels almost the same as the impure version.

This is the problem Monad solves.

Now once you have that description, eventually you want it to actually run for real so that side-effects and all other described pending behavior gets executed. But what people realized here is you can kind of abuse this a bit, where you could choose to interpret the description in new ways, or manipulate the description prior to executing it. Basically whatever executes the description for real is in charge of how to do that and it could choose to change some of the assumptions like the evaluation order or the thread on which thing execute, etc.

Thus once you have Monads, you can also use them to manipulate evaluation order and semantics.

Now if you don't care about constraining yourself to pure functions, all this can seem like overkill, and it kind of is.

Obviously, people will tell you it's a good idea to make everything pure, because you can more easily test things.

And people will tell you, even if you don't care about the purity, how Monads let you manipulate evaluation order and semantics of the code can be cool and allow for some neat features, so maybe that's something you'd find useful, such as adding tracing throughout automatically, or automatically parallelizing things that don't depend on each other, etc.

This is where some people say that Macros can often be used to similar effect. Because macros, a bit like Monads, operate on the description, macros on the code's AST itself, Monads on the captured Monadic composition. So a macro can also rewrite things to change their evaluation order or semantics.

I wouldn't call myself an expert on all this yet, more of an enthusiast, but that's how I would contextualize it.