It’s a container with certain behaviors and guarantees making them easy and reliable to manipulate and compose. A practical example is a generic List, that behaves like:
List[1, 2, 3], i.e. (“new”, “unit”, “wrap”) to create, containing obj(s)map(func) to transform objs inside, List[A] -> List[B]first(), i.e. (“unwrap”, “value”) to get back the objflat_map(func), i.e. (“bind”) to un-nest one level when func(a) itself produces another List, e.g. [3, 4].flat_map(get_divisors) == flatten_once([[1, 3], [1, 2, 4]]) == [1, 3, 1, 2, 4]Consider the code to do these things using for loops – the “business logic” func() would have to be embedded and interlaced with flow control. Things quickly get messy when there are multiple functions and edge cases like empty lists, and in those cases the List monad really helps clean things up.