I consider myself a “mostly functional” programmer. I
think about programs in terms of data flow and transformations and
mappings from input to output. I avoid side effects and mutable
state where it isn’t necessary. But I’m not a purist.
I’m happy to setf
an instance slot when it makes
sense.
Monads are the darling of the functional programming community. To hear it, they are the key to understanding the universe. They provide a way to encapsulate side effects and mutable state in a pure functional way.
But I don’t often use them.
There are a few reasons for this. I don’t work in Haskell, where monads are a ubiquitous part of the language. If you use Haskell, you’re going to use monads. But what about the languags that I do use? Monads of course “work” in any language that supports higher-order functions, but they are not always idiomatic or the obvious way to accomplish a task.
The primary use of monads is to mimic the look and feel of an
imperative language, with its sequential execution, side effects and
mutable state, in a language that is purely functional. Monads are
a way to implement progn
and setf
in
Haskell. But Lisp already has progn
and setf
.
And an imperative programming style is largely inferior to a
functional programming style. I prefer to not think imperatively
and write imperative code. I've seen many Haskell programs that
used monads just so they could use do
notation and
write code that looked like C. That seems like a step backwards.
Monads are complicated. They essentially come down to functional
composition of curried functions hidden behind some syntactic sugar.
So long as you don’t have to look under the hood, they are
reasonably easy to use. But when you do have to look under the
hood, you’ll find a maze of twisty, turny, higher-order
procedures. In Lisp, a well placed setf
can cut
through this functional Gordian knot.
2 comments:
I agree that doing imperative programming with monads is sometimes like going backwards. But at the same time having worked with non-pure languages (like TypeScript) I often wish I was using monads.
I'm assuming when you speak about Monads you actually mean doing I/O with Monads, because Monads are really useful for many data types like Either and Option. But setting that aside I think there are real benefits of Monads that make me wish I could use them more often:
- It's super nice to treat side-effects as first-class values. You can use higher-order effects that take other effects as input like a `retry` combinator.
- Monads are an abstraction, not a concrete implementation like `progn` or `setf`, which means that you can decide what "sequencing" means in your context.
- Last but not least, your imperative code becomes referentially transparent, which is already a huge deal IMO.
Here's a somewhat related post I thought you might like http://conal.net/blog/posts/can-functional-programming-be-liberated-from-the-von-neumann-paradigm
Well, we cannot do I/O without being imperative, right? I/O is a side effect, and the purely functional style cannot model side effects.
(ofc theoretically we can model side effects in the pure functional style by having an "environment" object which returns being changed after an I/O operation but it's not realistic to implement)
So until we switch away from von Neumann's machines the "functional core, imperative shell" is our unchangeable destiny, no need to be overly sad about it IMO.
Post a Comment