This [1] might help 'get it' with regards to monads. Very easy to digest.

You mention LINQ and the List abstraction. Yes IEnumerable<T> is a monad (LINQ isn't in itself - it [the grammar] is the equivalent of 'do' notation in Haskell). Monads are simply 'wrapper types' that follow a couple of rules:

1. You must be able to construct one from the un-wrapped value (return in Haskell, new List<T>(...) in C#)

2. It must implement the bind function.

If you understand 'map' (or Select in LINQ) then bind is very similar, except instead of returning a mapped version of the wrapped value, it returns a mapped version of the wrapped value, re-wrapped.

So in C# parlance (because I assume from your comment you use this daily):

IEnumerable<R> Map<T, R>(this IEnumerable<T> self, Func<T, R> mapper)
{
foreach(var x in self)
{
yield return mapper(x);
}
}

IEnumerable<R> Bind<T, R>(this IEnumerable<T> self, Func<T, IEnumerable<R>> binder)
{
foreach(var x in self)
{
foreach(var y in binder(x))
{
yield return y;
}
}
}

You may recognise that as SelectMany in LINQ. Select and SelectMany are special case function names in C# that allow the formation of LINQ expressions:

var res = from x in list
select x + 1;

Equates to:

var res = list.Select( x => x + 1);

And:

var res = from x in list1
from y in list2
select x + y;

Equates to:

var res = list1.SelectMany(x => list2.Select(y => x + y));

The result is a 'flattened' IEnumerable<T> - and that's why this is also known as flat-map.

It's been a while since I've done any Haskell, so forgive me if I get some of the syntax wrong here. But the LINQ statement above translates very similarly:

do x <- list1
y <- list2
return x + y

(I know there's a list comprehension syntax in Haskell, but I assume this is still right, Haskellers?)

The syntax is saying 'get the value out of its wrapper (the list) for me, so I can use it as-is (the add operation), then put the result back in a new wrapper'.

So why do we do all of this? It's so you can write the functions once that behave on the wrapped types, whether they're integers, strings or any type. The monad concept allows you to create a 'chain' of behaviour that from the outside is opaque, but internally it's operating on the raw wrapped values, as-is; and that makes all of the existing functions available. This is one of the key benefits.

A good analogy is to call them 'programmable semi-colons', because what makes each monad type what it is, is the behaviour of bind and return. So a list monad (as seen above) knows how to iterate the collection - so you don't have to write a for loop every time, an option monad knows when to not invoke the bind function if it's in a None state - so you don't have to test it yourself with an 'if' every time, the state monad knows how to propagate state changes - so you don't have to have extra context arguments on every function down the hierarchy, etc.

They remove boilerplate and capture common patterns. That is the other key benefit. At its core is an abstract idea, and I think that's why it's sometimes quite hard to grasp; but it's just a design pattern. Forget the category theory side of it, you don't need to know any of that to use use them or write new ones.

If you're more used to C# than Haskell, then you may want to check out my 'C# functional language extensions' library [2] that has C# implementations of the following monads:

Option

Either

State

Reader

Writer

And a few others, but that should be enough to get started. It may help you get your head around it in a language you're more familiar with?

[1] http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...

[2] https://github.com/louthy/language-ext