Reputation: 45642
I'm new to Clojure and finding my sea legs. I was wondering whether it is considered good or bad practice, from a functional programming standpoint, to put functions in Clojure maps and then pass those maps around like quasi-objects, as is often done in JavaScript. Explanations will also be appreciated.
Upvotes: 10
Views: 805
Reputation:
Namespaces are literally maps of functions. It's much more straightforward to use them to organize your functions. However, if you run into a limitation of namespaces for your use case, you could consider putting functions in a map.
As usual when going off the beaten track, just be sure to think hard about why you're not going the obvious route.
Upvotes: 1
Reputation: 10887
Clojure's multimethods are essentially maps of functions, so no, it's not a bad idea at all.
Upvotes: 7
Reputation: 106371
It works - and in some sense it's natural if you consider functions as first-class objects in the language (as all functional programmers should!)
However - it needs real care because what you're basically doing is interleaving code with data. It's rather like mixing your data model with presentation code in MVC. Yes, there may be some situations where it makes sense but the general principle would be to avoid it.
The style I'm slowly converging towards after about a year of Clojure is:
This solves most things under the sun, the one thing I'm still trying to figure out is the best way to create objects with highly polymorphic behaviour. Main options I can see are:
Still haven't worked out which way to go..... but I have a hunch that functions may sneak into maps a bit more in the future....
Upvotes: 4
Reputation: 17470
Do it, if it makes your code shorter or easier to understand, or test, or debug.
Or if you'd just like to. Trust your judgement.
Upvotes: 8
Reputation: 2649
If you really need to do object oriented programming in Clojure there are a couple of ways to do so.
The first way is to use the deftype
, defrecord
and defprotocol
family of macros.
The second way is to use multimethods, combined with a map or record type for data storage.
The third way is to use the universal design pattern as outlined by Steve Yegge, or a more Clojure specific introduction can be found in Chris Houser and Michael Fogus' book The Joy of Clojure.
The third approach is very similar to what you would expect to see in JavaScript, and the first approach, while not thought of as OOP in a traditional sense, is the most common in "modern" idiomatic Clojure.
Upvotes: 2
Reputation: 9508
It would be bad because of several reasons:
Long story short, if you're using Clojure to do OOP, you'll only get annoyed. You can do OOP more easily in tons of other languages, like say Groovy. If you do want to use Clojure do it the functional way.
So far I wrote not to do it and why not to do it. Now you might ask me: So, how should I write functions in Clojure?
Write functions that take your data structure (i.e. a map, list, object... whatever) as a parameter. So, instead of:
foo.bar();
You would define it like this:
(defn bar [foo]
;stuff
)
And call it like this:
(bar foo)
Now, for a pure function bar
, foo
object is unchanged after the function evaluation, and you don't have a shared state of the object to worry about if you decide to parallelize your code, which you would have if you were doing things the OOP way.
Also, it may look like a small difference, but note that the bar
function definition is an entity entirely independent of the data structure foo
. Also, foo
contains only the data, not behavior - all behavior is in the function. This separation gives you a much greater freedom when coding.
Upvotes: 6