Ted Neward on Twitter had the following somewhat astounding conversation:
Why do you keep insisting monads are goodness? What purpose do they serve in an impure functional environment like F#? (cite)
It’s just that if prospective F#’ers *have* to know monads to use F#, then F# is doomed. (cite)
If we’re OK with F# remaining niche, then OK, long live the monad! But I think it has more potential than that. (cite)
For a long time, I struggled with monads. I didn’t get what they were, or why I wanted to use them. I even had an awkard part in my Ruby.MN presentation of OCaml where I punted on them and then my helpful co-blogger pointed out I used them without realizing it in my presentation.
Since then, I have started to really grok monads, and it started with a realization: all of the Spring Inversion of Control stuff I’m so fond of are really monads done in an awkward OO style. So for all the scariness of the ominous name “monad”, and for all the hype about how awesome they are, they’re really just an idea the Java community is familiar (and happy) with expressed in a more direct way.
As an example, let’s consider an inversion of control class near and dear to my heart, JdbcTemplate. Now, JdbcTemplate is a collection of methods built off of a DataSource. While some of the methods are essentially utility methods (e.g. execute(String)), some of them are a bit more interesting. In particular, let’s look at query(String,RowCallbackHandler).
Used in Java, an example usage might be something like this:
final List<Integer> ints = new ArrayList<Integer>(); jdbcTemplate.query("select int_column from some_table", new RowCallbackHandler() { void processRow(ResultSet rs) throws SQLException { ints.add(rs.getInt(1)); } });
(Please note that there are more appropriate methods on JdbcTemplate to implement this behavior, but I wanted a simple yet non-trivial case to work from.)
Based on the Java, it’s not easy to see the monad — you kind of have to turn your head and squint. But things become clearer when you do the same code in Groovy.
def ints = [] jdbcTemplate.query("select int_column from some_table", { ResultSet rs -> ints << rs.getInt(1) } as RowCallbackHandler);
So now we see that what we’re really doing is passing a closure into the method — so we’re doing some kind of functional programming here, just with a veneer of OO. What we’re doing is being provided a pre-configured ResultSet that we can do some processing on, but which is managed by the surrounding context.
It’s easy to imagine the same kind of code in OCaml.
let ints = ref [];; let result_set_handler = fun rs -> ints := (get_int rs 1)::!ints in query jdbcTemplate "select int_column from some_table" result_set_handler;;
Or, put more monad-y…
let ints = ref [];; let jdbcTemplate = bind_data dataSource in let sqlTemplate = bind_sql jdbcTemplate "select int_column from some_table" in let result_set_handler = fun rs -> ints := (get_int rs 1)::!ints in let resultTemplate = bind_row_handler sqlTemplate result_set_handler in return_result resultTemplate;;
The monadic nature of inversion of control gets even more obvious if you consider a method like query(PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor rse) — it’s easy to envision a monad consisting of the DataSource, then binding to a a PreparedStatementCreator, then binding to a PreparedStatementSetter, then binding to a ResultSetExtractor, and finally returning. It is really nothing but a series of functions being bound into a context.
At this point, it should be clear that both monads and inversion of control are doing the same thing: they’re encapsulating context/change/state/patterns and providing stability by limiting the possible interactions with that context/change/state/pattern.
In the case of JdbcTemplate, it is encapsulating the standard patterns for working with JDBC calls, including whole swaths of nested try/catch blocks and error handling. By not allowing access to the connection or the prepared statement, it actually improves the clarity and stability of the code — after all, the connection and the prepare statement are implementation details, not meaningful parts of the algorithm.
Monads do the same thing: they express steps in the algorithm while abstracting away implementation details. Now, they’re much more powerful than the inversion of control pattern, because there isn’t this OO concept getting in the way of the pure functional goodness, but ultimately, monads and inversion of control are expressing the same kind of solution in different paradigms. These kinds of bridges are what we need to identify to help “line-of-business” developers move from OO to functional development.
Related posts:
Pingback: Why We Don’t Need Anonymous Inner Classes for Groovy/Grails | Enfranchised Mind
Pingback: Enfranchised Mind » Why We Don’t Need Anonymous Inner Classes for Groovy/Grails