>So something like C structs or rust enums?
C structs are not type safe at all, so no on that regard. And there's a strong preference for immutable structures over mutable ones, so no on that regard either. The immutability by default makes it easier to reason about things and it also makes testing easier, when compared with variables which might lose their value or change to something undesirable. Otherwise, yes, they are similar in the sense that they represent ad-hoc data structures.
Not really, there's some properties of monads which make programming certain things very clean and composable. The joke "A monad is just a monoid in the category of endofunctors, what's the problem?" really sums it up, a monad is just a class of objects that behaves like numbers under addition. Like 1 + 1 = 2, 1 + 0 = 1, except with "objects". The nice thing is that there's a lot of syntax sugar that makes working with monads nice. And because of type classes, you can hide a lot of the complexity of the ""addition"" to the description of the type itself.
This is kind of like making methods on objects. This is similar to OOP if all classes that have the trait "monad" had a ".join()" and a ".emptyConstructor()" method, but also all values are immutable. On top of that, Haskell has nice syntax which "pipe" one resulting monad to the next function. Which at the end means you can pipe things like `m1 >> func1 x y >> func2 x y` and the whole machinery will take care of itself. On top of that, if something is off, the compiler will tell you. This means you can offload a lot of complexity by blindly trusting the underlying implementation and build upon abstractions. This doesn't really translate to normal OOP, because everything needs to be modified "manually", described fairly mechanically, and you always need to have knowledge about all the moving parts.
Stuff like a failing chain of operations:
`m1 >> funcMightFail1 x >> funcMightFail2 >> funcMightFail3 >> funcMightFail4`
Where each function expects a non-failing parameter as input. The >> operator basically takes care of that for you. If at some point something fails, it just stops executing the rest of the functions. This is validated by types, by making your functions only accept non failing variables, you ensure that the >> operator is working correctly and that your program can never improperly arrive at such situation, and hence, you don't even consider it.
In an imperative setting this would have to look something like:
var x = funcMightFail1(x, m1);
if (!x) return null;
var y = funcMightFail2(x);
if (!y) return null;
var z = funcMightFail3(y);
if (!z) return null;
var k = funcMightFail3(y);
if (!k) return null;
// ... continue program
As you can see, that is extremely error prone and I even left a nice error in there for fun.