Got an email that A monad tutorial for Clojure programmers (part 1) was corrected with the latest changes around clojure.algo.monads. I almost immediately began reading it. It’s been a while since I read it, and recalled how tough it was to understand what monads are and how they work in Clojure. It was at the time when I just started my journey to Clojure land.
I skimmed through the article and it seemed so easy. I read it again and…nothing unusual for a Clojure beginner! Just a function call inside another and some syntactic sugar-like constructs with
I so well remember how tough it was back then. The anonymous function call with another inside was driving me crazy – there were certainly too many parens and my eyes bled. After almost two years with Clojure my eyes seem to have finally accustomed to read such “somewhat convoluted” expressions. I’m now able to uncover what’s inside with not much effort. Well, it’s not obvious at the moment I glance at it, but with some effort it just works.
This strange-looking expression
( (fn [a] ( (fn [b] (* a b)) (inc a) ) ) 1 ) in Clojure may’ve been easier when written as follows:
((fn [a] ((fn [b] (* a b)) (inc a))) 1)
You’ll have to parse the expression and build an AST in your mind. I assume you know that you can define a function with the fn special form.
user=> (doc fn) ------------------------- clojure.core/fn (fn name? [params*] exprs*) (fn name? ([params*] exprs*) +) Special Form params => positional-params* , or positional-params* & next-param positional-param => binding-form next-param => binding-form name => symbol Defines a function Please see http://clojure.org/special_forms#fn nil user=> (fn [a] a) #<user$eval107$fn__108 user$eval107$fn__108@2dcc9f86>
The function (of a single argument “a” that’s simply returned with no change) is defined but immediately gone with no way of calling. You’ll usually bind it to a symbol.
Clojure is a functional programming language so functions are first-class citizens. You know you can assign a value to a variable in Java. A function in Clojure is another value (amongst the others you may already have come across in other languages). If you can bind a symbol to a value (and hence establish a mapping between a string and a value), you can do so with functions too in Clojure.
user=> (def f (fn [a] a)) #'user/f
I’m sure you know how to call it – have this symbol
f (that’s bound to a Var that’s in turn bound to a function) be the first item of a list.
user=> (f 2) 2
Easy, isn’t it? It should.
Going back to the initial problem of reading anonymous function’s calls inside other function calls, find the very nested
fn. The list with the first element being
fn is an anonymous function definition. Should you want to call it, wrap it inside a pair of parens.
user=> ((fn [a] a)) ArityException Wrong number of args (0) passed to: user$eval118$fn clojure.lang.AFn.throwArity (AFn.java:437)
An exception?! Of course! This anonymous function expects a single input argument and hence the exception – you’ve just called a function of a single argument with no argument given. It’s obviously an exception, and should come with no surprise.
user=> ((fn [a] a) 2) 2
That’s much better now. No exceptions, many parens – you’re safe at home :)
Since a function can call another function and the “inside” function can again be an anonymous function (defined by the
fn form), you’ll end up with the nested
fn forms which are executed in-place – where the anonymous functions are defined. That’s what’s presented in the article.
((fn [a] ((fn [b] (* a b)) (inc a))) 1)
I think however that it’ll be easier to see it with the following function definition.
user=> (defn f [a] (fn [b] (* a b))) #'user/f user=> ((f 2) 3) 6
f of a single input argument “a” returns a function of a single argument “b” that in turn returns a multiplication of “a” and “b”. Although it’s not necessarily the exact rewrite of the initial method call it should give you the idea of different ways of defining and executing functions in Clojure.
I also hope it will help you out on your way to Clojure and monads nirvana. On to reading the part 2. They say monads are challenging, but it may’ve been that Clojure makes them a breeze (so far).