There is an abstraction that is less powerful than a Monad but which already makes your life better: Functors.
Imagine you want to write a function that adds 3 to its argument. Easy.
Now however assume that the argument could be nil in which case you just want to reply with nil.
(defun foo1 (n)
(when-not (nil? n)
(+ n 3)))
And now let’s say that you want to write a similar function where you have a list of numbers and want to add 3 to each of them:
(defun foo2 (nums)
(map #(+ % 3) nums))
Or what if you want to write a function to which you pass another function which either returns a number or which throws an exception?
(defun foo3 (f)
(try
(+ (f) 3)
(catch e ...)))
Or you have a hashmap in which all the values are numbers and you want to add 3 to each of them and thus generate a new map:
(defun foo4 (hm)
(zipmap (keys hm) (map #(+ % 3) (vals hm))))
In Haskell you would be write an implementation against Functors. In Clojure they exist too, as well as monads. So in Haskell you would express it like this:
foo = fmap (+3)
And in Clojure you could maybe say:
(def foo (partial fmap #(+ % 3)))
And that’s it.
This foo can potentially replace all of the foo versions above. So it is much more reusable. Composability is the holy grail of functional programming and monads compose very well.