I’ve been hacking some Clojure code the last couple days and something I’m really coming to love is Clojure’s terse syntax for lambda functions.
The basic form is pretty darn short…
…but we can make that even shorter with this syntax that doesn’t even require us to name the argument:
…or, if we have multiple arguments:
Which makes a lot of sense if you think about it; we’re using a lambda function in the first place to avoid needlessly giving a function a name—why not extend that logic to the arguments? (Obviously don’t take that to extremes…)
This reminds me a bit of Ruby’s clever
&:sym idiom, which makes it significantly more convenient
to invoke methods on arrays of objects:
1 2 3 4 5 6
If, however, we’re dealing with the fairly common case of wanting to extract an array of values from an array of hashes, we’re back to the “long” form:
1 2 3 4 5
Functional (and quasi-functional) languages tend to have the terseness of the first case as a sort of freebie because they implement methods as functions over objects (or just use non-method functions for similar scenarios):
But Clojure does something really nifty in the second case where we were retrieving values from
hashes. Clojure provides callability via an extensible abstraction called a
Maps—what I’ve been loosely referring to as
“hashes”—implement this protocol. This means that we can index into them as if we were making a
1 2 3 4 5
Notice how the
Map occupies the first position in list—the spot where we’d normally expect to
see a function (or macro).
And that’s nice and all. But what’s really cool is that because keywords (e.g.
:baz) are used so frequently as hash keys, they too implement the callability protocol:
1 2 3 4 5 6 7 8
Love it. Regardless of if I’m using hashes or objects it’s succinct and beautiful.
Now, extending callability is one of those black magics which should be used sparingly, but for educational purposes—EDUCATIONAL!—let’s dig into how such a thing is implemented. Let’s create a class to represent a shell command and make it directly callable, with the ability to pass command-line arguments.
The protocol we need to implement to make our class callable is called
clojure.lang.IFn (I’m not
entirely sure where that name comes from—my best guess is that ‘I’ is a prefix indicating
interface and ‘Fn’ is to signify that this is the function calling interface). We’ll use
defrecord to create a Java class for our callable objects. Here’s
some (heavily-annotated) source code that should do the trick:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Now let’s run it:
1 2 3 4 5 6 7
And it works! Have any clever uses for making objects callable? Maybe some kind of (stateful) synchronization or remote-invokation constructs?