Eh... Monads aren't really just about side effects. They're really just a generalization of the threading macros in Clojure (sort of, at least I view them with the same motivation as the threading macros and their cousins in Closure). As for logging, if it's really just a log statement I'll sometimes just do the equivalent of `unsafePerformIO`ing it and not worry about it. All depends on what you consider semantically meaningful behavior from the program.
On the other hand, with the rise of stuff like OpenTracing and structured logging I think the Haskell community was pretty prescient about treating logging as an explicit side effect.
I agree that monads aren't just about side effects, and lots of languages use monadic patterns. My point was that using monadic patterns is prevalent in Haskell specifically due to using the type system to track side effects such as IO. Clojure has monadic libraries like cats, that let you write Haskell style code, but they're not popular because in most cases you can solve the problem in a more direct way.
Well the monadic structure is always there, it's just a matter of whether you use it or not :).
For the most part I admire the Clojure community's focus on data and wariness of higher order abstractions. Whether it be classes, typeclasses, or higher order functions, if you can express it with just data it's almost always better and most communities would do well to remember that.
On the flip side when there is a need for higher order abstractions, I sometimes find Clojure's standard tools to be lacking. Speaking of monadic structure being there whether you use them or not, transducers are a great example. I find them overcomplicated in Clojure, which I think is due to focusing too heavily on using standard function composition to compose them. I regard this as a bit of a trick or a coincidence. When's the last time you composed something with a transducer that wasn't just another transducer as opposed to an arbitrary function? In fact if you instead use monadic composition (i.e. compose `a -> m b` with `b -> m c` to get `a -> m c`, in this case `m` is just `List`) you'll find that transducers are just functions of the form `a -> List b` rather than higher order functions. And yes that `List` remains there even though transducers work on things that aren't just concrete collections.
I've been meaning to try to push out a Clojure library that shows this but haven't gotten around to it. Maybe this thread will be the kick I need.
Yup, there are always trade offs with every approach. A monadic version of transducers would be neat, and it would be fun to contrast them with the HOF approach in terms of pros and cons. :)
Well hopefully the monadic structure would be hidden. The only difference would be a custom composition operator instead of function composition. You could interop between the two representations with conversion functions.
On the other hand, with the rise of stuff like OpenTracing and structured logging I think the Haskell community was pretty prescient about treating logging as an explicit side effect.